Compare commits

..

2 Commits

Author SHA1 Message Date
Dallin Romney
44c356e002 docs: document native approval route gates 2026-05-27 14:27:30 -07:00
Dallin Romney
31ccfb7d95 refactor: share native approval route gates 2026-05-27 13:55:51 -07:00
640 changed files with 5647 additions and 17938 deletions

View File

@@ -50,9 +50,8 @@ Dirty local work:
```
Use this only when the patch is actually unstaged/staged/untracked in the
current checkout. `--mode uncommitted` is accepted as an alias for `--mode local`.
For committed, pushed, or PR work, point the helper at the commit
or branch diff instead; do not force dirty modes just
current checkout. For committed, pushed, or PR work, point the helper at the commit
or branch diff instead; do not force `--mode local` / `--uncommitted` just
because the helper docs mention dirty work first. A clean local review
only proves there is no local patch.
@@ -165,7 +164,6 @@ If installed from `agent-scripts`, path is:
The helper:
- chooses dirty local changes first
- accepts `--mode uncommitted` as an alias for `--mode local`
- otherwise uses current PR base if `gh pr view` works
- otherwise uses `origin/main` for non-main branches
- supports `--engine codex`, `claude`, `droid`, and `copilot`; default is `AUTOREVIEW_ENGINE` or `codex`; Codex should remain the default when nothing is set

View File

@@ -238,7 +238,6 @@ def is_dirty(repo: Path) -> bool:
def choose_target(repo: Path, mode: str, base_ref: str | None) -> tuple[str, str | None]:
mode = "local" if mode == "uncommitted" else mode
branch = current_branch(repo)
if mode == "local" or (mode == "auto" and is_dirty(repo)):
return "local", None
@@ -890,7 +889,7 @@ def finish_parallel_tests(proc: subprocess.Popen, started: float) -> int:
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Bundle-driven AI code review.")
parser.add_argument("--mode", choices=["auto", "local", "uncommitted", "branch", "commit"], default="auto")
parser.add_argument("--mode", choices=["auto", "local", "branch", "commit"], default="auto")
parser.add_argument("--base")
parser.add_argument("--commit", default="HEAD")
parser.add_argument("--engine", choices=ENGINES, default=os.environ.get("AUTOREVIEW_ENGINE", "codex"))

View File

@@ -46,8 +46,6 @@ preserving issue/PR refs and thanks.
- `### Fixes`: user-facing fixes first, grouped by impact and surface
6. Preserve attribution:
- keep `#issue`, `(#PR)`, `Fixes #...`, and `Thanks @...`
- every human-authored merged PR represented by a user-facing entry needs
its PR ref and `Thanks @author`, even when the PR had no linked issue
- do not add GHSA references, advisory IDs, or security advisory slugs to
changelog entries or GitHub release-note text unless explicitly requested
- never thank bots, `@openclaw`, `@clawsweeper`, or `@steipete`

3
.gitattributes vendored
View File

@@ -1,6 +1,3 @@
* text=auto eol=lf
CLAUDE.md -text
src/gateway/server-methods/CLAUDE.md -text
ui/src/i18n/.i18n/* linguist-generated
ui/src/i18n/locales/*.ts linguist-generated
ui/src/i18n/locales/en.ts -linguist-generated

View File

@@ -30,7 +30,6 @@
"docker-compose.yml",
"dist/",
"docs/_layouts/",
"extensions/diffs/assets/viewer-runtime.js",
"**/*.json",
"node_modules/",
"patches/",

View File

@@ -211,7 +211,6 @@ Skills own workflows; root owns hard policy and routing.
- Lockfiles/shrinkwrap are security surface: review `pnpm-lock.yaml`, `npm-shrinkwrap.json`, `package-lock.json`; root/plugin npm packages ship shrinkwrap, not package-lock.
- Carbon pins owner-only: do not change `@buape/carbon` unless Shadow (`@thewilloftheshadow`, verified by `gh`) asks.
- Releases/publish/version bumps need explicit approval. Use `$release-openclaw-maintainer`.
- Backport means apply to newest open `release/` branch unless user names another target.
- GHSA/advisories: `$openclaw-ghsa-maintainer` / `$security-triage`. Secret scanning: `$openclaw-secret-scanning-maintainer`.
- Beta tag/version match: `vYYYY.M.D-beta.N` -> npm `YYYY.M.D-beta.N --tag beta`.

View File

@@ -2,32 +2,6 @@
Docs: https://docs.openclaw.ai
## 2026.5.28
### Highlights
- Agent and Codex runtime recovery is steadier: subagents keep cwd/workspace separation, hook context stays prompt-local, session locks release on timeout abort, stale restart continuations are avoided, and Codex app-server/helper failures no longer tear down shared runtime state. (#87218, #86875, #87409, #87399, #87375)
- Channel delivery and session identity got safer across outbound plugin hooks, Matrix room ids, iMessage reactions/approvals, Slack final replies, Discord recovered tool warnings, and Microsoft Teams service URL trust checks. (#73706, #75670, #87366, #87451, #87334)
- CLI, auth, doctor, and provider paths fail faster and recover more clearly: malformed numeric/version options are rejected, OAuth and local service startup requests are bounded, legacy `api_key` auth profiles migrate to canonical form, and restart guidance is actionable. (#87398, #86281, #87361)
- Plugin and Gateway hot paths do less repeated work while preserving cache correctness for install records, config JSON parsing, tool search catalogs, session stores, manifest model rows, auto-enabled plugin config, browser tokens, and viewer assets. (#86699)
- Release, QA, and E2E validation now bound more log, artifact, harness, and cross-OS waits so failing lanes produce proof instead of hanging or false-greening.
### Changes
- Status: show active subagent details in status output.
- Diffs: split the default language pack and expand default Diffs language coverage while keeping the host floor aligned. (#87370, #87372) Thanks @RomneyDa.
- ClawHub: add plugin display names plus skill verification and trust surfaces. (#87354, #86699) Thanks @thewilloftheshadow and @Patrick-Erichsen.
- Docs: clarify Codex computer-use setup, paste-token stdin auth setup, macOS gateway sleep troubleshooting, native Codex hook relay recovery, container model auth, install deployment cards, device-token admin gating, and backport targets. (#87313, #63050) Thanks @bdjben, @liaoandi, and @thewilloftheshadow.
### Fixes
- Agents/Codex: keep spawned agent cwd/workspace state separated, keep hook context prompt-local, release session locks on timeout abort, avoid session event queue self-wait, preserve shared app-server state across startup or helper failures, keep native hook relay alive across restarts, route workspace memory through tools, resolve Codex runtime models first, report quarantined dynamic tools, format `skills` command output, and bound compaction/steering retries. (#87218, #86875, #86123, #87399, #87375, #87383, #87400) Thanks @mbelinky, @Alix-007, @luoyanglang, @yetval, and @sjf.
- Channels: thread canonical session keys into outbound hooks, preserve Matrix room-id case, keep fallback tool warnings mention-inert, retain delivered Slack final replies during late cleanup, continue iMessage polling after denied reactions, suppress duplicate native exec approvals, preserve Telegram SecretRef prompt config, suppress Discord recovered tool warnings, and block untrusted Teams service URLs. (#73706, #75670, #87366, #87451, #87334) Thanks @zeroaltitude, @lukeboyett, @xiaotian, and @eleqtrizit.
- CLI/auth/doctor/providers: reject malformed numeric/timeout/subcommand-version inputs, wait for respawn child shutdown, bound Codex and GitHub Copilot OAuth/token requests, warm provider auth off the main thread, honor Codex response timeouts, bound local service startup, resolve GPT-5.5 without cached catalog, migrate legacy memory auto-provider config, rewrite non-canonical `api_key` auth profiles, and make doctor restart follow-ups actionable. (#87398, #86281, #87361) Thanks @Patrick-Erichsen, @samzong, @giodl73-repo, and @alkor2000.
- Gateway/security/session state: expire browser tokens after auth rotation, scope assistant idempotency dedupe, drain probe client closes, avoid stale restart continuation reuse, preserve retry-after fallbacks, bound webchat image and artifact transcript scans, include seconds in inbound metadata timestamps, and evict current plugin-state namespaces at row caps.
- Performance: trust install-record caches between reloads, prefer native JSON parsing, reuse unchanged tool-search catalogs, skip unchanged store serialization, add precomputed session patch writers, reduce store clone allocations, cache manifest model catalog rows and auto-enabled plugin config, and slim current metadata identity caches.
- Docker/release/QA: package runtime workspace templates, stream cross-OS served artifacts, preserve sparse Crabbox run artifacts, bound OpenClaw instance logs, plugin gauntlet relay logs, MCP channel buffers, kitchen-sink scans, agent-turn assertions, and release scenario logs, and keep release/google live guards current.
## 2026.5.27
### Highlights
@@ -48,15 +22,12 @@ Docs: https://docs.openclaw.ai
### Fixes
- Security/CLI/runtime: harden hostname normalization for repeated trailing dots, block side-effecting command wrappers, reject unsafe Node runtime env overrides, reject loose numeric CLI and gateway options, require admin approval for node device-role pairing, and reject no-auth Tailscale exposure. (#87305, #87292, #87308, #87146) Thanks @pgondhi987.
- Doctor: validate runtime tool schemas for every configured embedded agent while skipping ACP-only profiles, so bad non-default plugin or MCP tools are reported before assistant turns.
- Telegram: route `sendMessage` action replies through durable outbound delivery so completed agent responses remain retryable when the gateway send path times out. (#87261) Thanks @mbelinky.
- Matrix/auto-reply: keep draft previews mention-inert, preserve final mention delivery, send mention finals normally, await shared DM notices, ignore filename-embedded MXIDs, and suppress reasoning-prefixed `NO_REPLY` responses.
- Agents/providers: add OpenAI-compatible cache retention, forward cached token usage in chat completions, preserve runtime context before active user turns, strip stale Anthropic thinking, load Claude CLI OAuth for Pi auth profiles, avoid false Codex runtime live switches, and quarantine unsupported tool schemas. (#82062, #87167, #86855)
- Gateway/performance: cache plugin metadata fingerprints and stable plugin index fingerprints, borrow read-only session metadata safely, keep the active session working store hot, keep status on a bounded fast path, and preserve model auth profile suffixes. (#86439)
- Package/install/release: align npm package exclusions and inventory, omit unpacked test helpers, skip Homebrew until macOS packages need it, cap tsdown heap in containers, bound install/release smoke waits, and harden post-publish verification.
- Codex/Auth: bound ChatGPT OAuth token exchange and refresh requests, and honor cancellation across Codex and Anthropic OAuth login flows.
- QA/E2E/CI: bound Telegram, kitchen-sink, Open WebUI, ClawHub, MCP, Discord, realtime, labeler, and GitHub API waits; fail empty explicit test, live-media, gateway CPU, plugin gauntlet, and beta-smoke runs instead of false-greening.
- Agents/Codex: keep spawned agent bootstrap files rooted in the agent workspace while running task commands, transcripts, and compaction from the requested cwd. (#87218) Thanks @mbelinky.
## 2026.5.26
@@ -90,7 +61,7 @@ Docs: https://docs.openclaw.ai
- Voice: share activation-name matching and consult-transcript screening through the realtime voice SDK so Discord, browser voice, and meeting surfaces can reuse one implementation.
- Cron: default `cron.maxConcurrentRuns` to 8 so scheduled automations and their isolated agent turns can make progress in parallel without explicit configuration.
- QA-Lab: add `qa coverage --match <query>` so focused proof selection can discover matching scenarios from existing metadata before running live or remote lanes.
- Discord/model picker: surface an alpha-bucket select (e.g. `AG (12) · HN (18) · OZ (5)`) when the provider list or a provider's model list exceeds 25 items, so configs with `provider/*` wildcards stay one click from the right page instead of paginating through prev/next; falls back to numeric chunks when every item shares the same first letter. (#86181) Thanks @rendrag-git.
- Discord/model picker: surface an alpha-bucket select (e.g. `AG (12) · HN (18) · OZ (5)`) when the provider list or a provider's model list exceeds 25 items, so configs with `provider/*` wildcards stay one click from the right page instead of paginating through prev/next; falls back to numeric chunks when every item shares the same first letter.
- Control UI: add an ephemeral Activity tab for sanitized live tool activity summaries without persisting raw telemetry. Fixes #12831. Thanks @BunsDev.
- Build: include `ui:build` in the `full` and `ciArtifacts` profiles of `scripts/build-all.mjs` so `pnpm build` always rebuilds `dist/control-ui` after `tsdown` cleans `dist`, removing the second-command requirement and the missing-asset failure mode for source/runtime installs and CI artifact uploads. (#85206)
- iOS: improve Talk mode with direct realtime voice sessions, compact toolbar status, and responsive voice waveform feedback. (#86355) Thanks @ngutman.

View File

@@ -65,8 +65,8 @@ android {
applicationId = "ai.openclaw.app"
minSdk = 31
targetSdk = 36
versionCode = 2026052801
versionName = "2026.5.28"
versionCode = 2026052701
versionName = "2026.5.27"
ndk {
// Support all major ABIs — native libs are tiny (~47 KB per ABI)
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")

View File

@@ -1,9 +1,5 @@
# OpenClaw iOS Changelog
## 2026.5.28 - 2026-05-28
Maintenance update for the current OpenClaw release.
## 2026.5.27 - 2026-05-27
Maintenance update for the current OpenClaw release.

View File

@@ -2,8 +2,8 @@
// Source of truth: apps/ios/version.json
// Generated by scripts/ios-sync-versioning.ts.
OPENCLAW_IOS_VERSION = 2026.5.28
OPENCLAW_MARKETING_VERSION = 2026.5.28
OPENCLAW_IOS_VERSION = 2026.5.27
OPENCLAW_MARKETING_VERSION = 2026.5.27
OPENCLAW_BUILD_VERSION = 1
#include? "../build/Version.xcconfig"

View File

@@ -1,3 +1,3 @@
{
"version": "2026.5.28"
"version": "2026.5.27"
}

View File

@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.5.28</string>
<string>2026.5.27</string>
<key>CFBundleVersion</key>
<string>2026052800</string>
<string>2026052700</string>
<key>CFBundleIconFile</key>
<string>OpenClaw</string>
<key>CFBundleURLTypes</key>

View File

@@ -2177,7 +2177,6 @@ public struct SessionsPatchParams: Codable, Sendable {
public let model: AnyCodable?
public let spawnedby: AnyCodable?
public let spawnedworkspacedir: AnyCodable?
public let spawnedcwd: AnyCodable?
public let spawndepth: AnyCodable?
public let subagentrole: AnyCodable?
public let subagentcontrolscope: AnyCodable?
@@ -2203,7 +2202,6 @@ public struct SessionsPatchParams: Codable, Sendable {
model: AnyCodable?,
spawnedby: AnyCodable?,
spawnedworkspacedir: AnyCodable?,
spawnedcwd: AnyCodable?,
spawndepth: AnyCodable?,
subagentrole: AnyCodable?,
subagentcontrolscope: AnyCodable?,
@@ -2228,7 +2226,6 @@ public struct SessionsPatchParams: Codable, Sendable {
self.model = model
self.spawnedby = spawnedby
self.spawnedworkspacedir = spawnedworkspacedir
self.spawnedcwd = spawnedcwd
self.spawndepth = spawndepth
self.subagentrole = subagentrole
self.subagentcontrolscope = subagentcontrolscope
@@ -2255,7 +2252,6 @@ public struct SessionsPatchParams: Codable, Sendable {
case model
case spawnedby = "spawnedBy"
case spawnedworkspacedir = "spawnedWorkspaceDir"
case spawnedcwd = "spawnedCwd"
case spawndepth = "spawnDepth"
case subagentrole = "subagentRole"
case subagentcontrolscope = "subagentControlScope"
@@ -4994,51 +4990,25 @@ public struct ToolsEffectiveGroup: Codable, Sendable {
}
}
public struct ToolsEffectiveNotice: Codable, Sendable {
public let id: String
public let severity: AnyCodable
public let message: String
public init(
id: String,
severity: AnyCodable,
message: String)
{
self.id = id
self.severity = severity
self.message = message
}
private enum CodingKeys: String, CodingKey {
case id
case severity
case message
}
}
public struct ToolsEffectiveResult: Codable, Sendable {
public let agentid: String
public let profile: String
public let groups: [ToolsEffectiveGroup]
public let notices: [ToolsEffectiveNotice]?
public init(
agentid: String,
profile: String,
groups: [ToolsEffectiveGroup],
notices: [ToolsEffectiveNotice]?)
groups: [ToolsEffectiveGroup])
{
self.agentid = agentid
self.profile = profile
self.groups = groups
self.notices = notices
}
private enum CodingKeys: String, CodingKey {
case agentid = "agentId"
case profile
case groups
case notices
}
}
@@ -5982,6 +5952,7 @@ public struct PluginApprovalRequestParams: Codable, Sendable {
public let toolname: String?
public let toolcallid: String?
public let alloweddecisions: [String]?
public let actions: [[String: AnyCodable]]?
public let agentid: String?
public let sessionkey: String?
public let turnsourcechannel: String?
@@ -5990,6 +5961,7 @@ public struct PluginApprovalRequestParams: Codable, Sendable {
public let turnsourcethreadid: AnyCodable?
public let timeoutms: Int?
public let twophase: Bool?
public let keeppendingwithoutroute: Bool?
public init(
pluginid: String?,
@@ -5999,6 +5971,7 @@ public struct PluginApprovalRequestParams: Codable, Sendable {
toolname: String?,
toolcallid: String?,
alloweddecisions: [String]?,
actions: [[String: AnyCodable]]?,
agentid: String?,
sessionkey: String?,
turnsourcechannel: String?,
@@ -6006,7 +5979,8 @@ public struct PluginApprovalRequestParams: Codable, Sendable {
turnsourceaccountid: String?,
turnsourcethreadid: AnyCodable?,
timeoutms: Int?,
twophase: Bool?)
twophase: Bool?,
keeppendingwithoutroute: Bool?)
{
self.pluginid = pluginid
self.title = title
@@ -6015,6 +5989,7 @@ public struct PluginApprovalRequestParams: Codable, Sendable {
self.toolname = toolname
self.toolcallid = toolcallid
self.alloweddecisions = alloweddecisions
self.actions = actions
self.agentid = agentid
self.sessionkey = sessionkey
self.turnsourcechannel = turnsourcechannel
@@ -6023,6 +5998,7 @@ public struct PluginApprovalRequestParams: Codable, Sendable {
self.turnsourcethreadid = turnsourcethreadid
self.timeoutms = timeoutms
self.twophase = twophase
self.keeppendingwithoutroute = keeppendingwithoutroute
}
private enum CodingKeys: String, CodingKey {
@@ -6033,6 +6009,7 @@ public struct PluginApprovalRequestParams: Codable, Sendable {
case toolname = "toolName"
case toolcallid = "toolCallId"
case alloweddecisions = "allowedDecisions"
case actions
case agentid = "agentId"
case sessionkey = "sessionKey"
case turnsourcechannel = "turnSourceChannel"
@@ -6041,6 +6018,7 @@ public struct PluginApprovalRequestParams: Codable, Sendable {
case turnsourcethreadid = "turnSourceThreadId"
case timeoutms = "timeoutMs"
case twophase = "twoPhase"
case keeppendingwithoutroute = "keepPendingWithoutRoute"
}
}

View File

@@ -10,7 +10,6 @@ const rootEntries = [
"src/entry.ts!",
"src/cli/daemon-cli.ts!",
"src/agents/code-mode.worker.ts!",
"src/agents/model-provider-auth.worker.ts!",
"src/infra/kysely-node-sqlite.ts!",
"src/infra/warning-filter.ts!",
"src/infra/command-explainer/index.ts!",
@@ -157,7 +156,12 @@ const config = {
],
},
ui: {
entry: ["index.html!", "src/main.ts!", "vite.config.ts!", "vitest*.ts!"],
entry: [
"index.html!",
"src/main.ts!",
"vite.config.ts!",
"vitest*.ts!",
],
project: ["src/**/*.{ts,tsx}!"],
},
"packages/sdk": {

View File

@@ -1,4 +1,4 @@
a69acd971a7d54d3086f26c52fde4084eaeef350f71b918fb8e7338f329bff95 config-baseline.json
ee4c0f0fb15cda02268f2e83d0c5e1c8d0ec0a2c1b2fdb89cdfce308dadb2b8b config-baseline.core.json
de712076969bd63086959bf61c20a7581e5cae5b6982ffe83eefcc5b47ad8700 config-baseline.json
13fb390fd71a8d456cdfd42e6d9e577eba286e4509cc4e1a11c42f2e19255514 config-baseline.core.json
b901fb766edfd9df630690281476fc4032c64772f69d1d8f7b2e0e913a90f229 config-baseline.channel.json
1b763a5524aca2d7ecf1eea38f845ad1ffed5c1b37e85e62f6a7902a3ee0f920 config-baseline.plugin.json
1c6b972bd2c4caf936729c2a898a70b010dfedec0700eedb2140d6ebbf4fd3d3 config-baseline.plugin.json

View File

@@ -1,2 +1,2 @@
7039b60f2cea732a90db633328952faaddd919f0d098b303b29d554e64184073 plugin-sdk-api-baseline.json
1a78f4df81562af070c5379c6369a8bea9c704f985b5382a463364757b26db0d plugin-sdk-api-baseline.jsonl
fb007e48bdcd035d025fd3442f753268e40c6947ddd11a25405b6a259ff25c45 plugin-sdk-api-baseline.json
d1bb1847131b36ccc4f3813c68ac6ef7621247284c9366652ebcdd29aa327b3f plugin-sdk-api-baseline.jsonl

View File

@@ -12,7 +12,7 @@ OpenClaw standardizes timestamps so the model sees a **single reference time** i
| Surface | What it shows | Default | Configured via |
| ----------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------- | ------------------------------------------------------- |
| Message envelopes | Wraps inbound channel messages: `[Signal +1555 Sun 2026-01-18 00:19:42 PST] hello` | Host-local | `agents.defaults.envelopeTimezone` |
| Message envelopes | Wraps inbound channel messages: `[Signal +1555 2026-01-18 00:19 PST] hello` | Host-local | `agents.defaults.envelopeTimezone` |
| Tool payloads | Channel `readMessages`-style tools return raw provider time + normalized `timestampMs` / `timestampUtc` | UTC fields always present | Not configurable — preserves provider-native timestamps |
| System prompt | A small `Current Date & Time` block with the **time zone only** (no clock value, for cache stability) | Host timezone if `userTimezone` unset | `agents.defaults.userTimezone` |

View File

@@ -11,10 +11,10 @@ Provider timestamps are preserved so tools keep their native semantics (current
## Message envelopes (local by default)
Inbound messages are wrapped with a timestamp (second precision):
Inbound messages are wrapped with a timestamp (minute precision):
```
[Provider ... Mon 2026-01-05 16:26:34 PST] message text
[Provider ... 2026-01-05 16:26 PST] message text
```
This envelope timestamp is **host-local by default**, regardless of the provider timezone.
@@ -45,19 +45,19 @@ You can override this behavior:
**Local (default):**
```
[WhatsApp +1555 Sun 2026-01-18 00:19:42 PST] hello
[WhatsApp +1555 2026-01-18 00:19 PST] hello
```
**User timezone:**
```
[WhatsApp +1555 Sun 2026-01-18 00:19:42 CST] hello
[WhatsApp +1555 2026-01-18 00:19 CST] hello
```
**Elapsed time enabled:**
```
[WhatsApp +1555 +30s Sun 2026-01-18T05:19:00Z] follow-up
[WhatsApp +1555 +30s 2026-01-18T05:19Z] follow-up
```
## System prompt: current date and time

View File

@@ -515,7 +515,7 @@ That stages grounded durable candidates into the short-term dreaming store while
- **QMD backend**: probes whether the `qmd` binary is available and startable. If not, prints fix guidance including the npm package and a manual binary path option.
- **Explicit local provider**: checks for a local model file or a recognized remote/downloadable model URL. If missing, suggests switching to a remote provider.
- **Explicit remote provider** (`openai`, `voyage`, etc.): verifies an API key is present in the environment or auth store. Prints actionable fix hints if missing.
- **Legacy auto provider**: treats `memorySearch.provider: "auto"` as OpenAI, checks OpenAI readiness, and `doctor --fix` rewrites it to `provider: "openai"`.
- **Legacy auto provider**: treats `memorySearch.provider: "auto"` as OpenAI and checks OpenAI readiness.
When a cached gateway probe result is available (gateway was healthy at the time of the check), doctor cross-references its result with the CLI-visible config and notes any discrepancy. Doctor does not start a fresh embedding ping on the default path; use the deep memory status command when you want a live provider check.

View File

@@ -183,12 +183,81 @@ local proof.
</Step>
</Steps>
<a id="registering-agent-tools"></a>
## Plugin capabilities
## Registering tools
A single plugin can register any number of capabilities via the `api` object:
Tools can be required or optional. Required tools are always available when the
plugin is enabled. Optional tools require user opt-in.
| Capability | Registration method | Detailed guide |
| ---------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------- |
| Text inference (LLM) | `api.registerProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins) |
| CLI inference backend | `api.registerCliBackend(...)` | [CLI Backend Plugins](/plugins/cli-backend-plugins) |
| Channel / messaging | `api.registerChannel(...)` | [Channel Plugins](/plugins/sdk-channel-plugins) |
| Speech (TTS/STT) | `api.registerSpeechProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Realtime transcription | `api.registerRealtimeTranscriptionProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Realtime voice | `api.registerRealtimeVoiceProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Media understanding | `api.registerMediaUnderstandingProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Image generation | `api.registerImageGenerationProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Music generation | `api.registerMusicGenerationProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Video generation | `api.registerVideoGenerationProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Web fetch | `api.registerWebFetchProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Web search | `api.registerWebSearchProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
| Tool-result middleware | `api.registerAgentToolResultMiddleware(...)` | [SDK Overview](/plugins/sdk-overview#registration-api) |
| Agent tools | `api.registerTool(...)` | Below |
| Custom commands | `api.registerCommand(...)` | [Entry Points](/plugins/sdk-entrypoints) |
| Plugin hooks | `api.on(...)` | [Plugin hooks](/plugins/hooks) |
| Internal event hooks | `api.registerHook(...)` | [Entry Points](/plugins/sdk-entrypoints) |
| HTTP routes | `api.registerHttpRoute(...)` | [Internals](/plugins/architecture-internals#gateway-http-routes) |
| CLI subcommands | `api.registerCli(...)` | [Entry Points](/plugins/sdk-entrypoints) |
For the full registration API, see [SDK Overview](/plugins/sdk-overview#registration-api).
Bundled plugins can use `api.registerAgentToolResultMiddleware(...)` when they
need async tool-result rewriting before the model sees the output. Declare the
targeted runtimes in `contracts.agentToolResultMiddleware`, for example
`["pi", "codex"]`. This is a trusted bundled-plugin seam; external
plugins should prefer regular OpenClaw plugin hooks unless OpenClaw grows an
explicit trust policy for this capability.
If your plugin registers custom gateway RPC methods, keep them on a
plugin-specific prefix. Core admin namespaces (`config.*`,
`exec.approvals.*`, `wizard.*`, `update.*`) stay reserved and always resolve to
`operator.admin`, even if a plugin asks for a narrower scope.
`openclaw/plugin-sdk/gateway-method-runtime` is a reserved control-plane bridge
for plugin HTTP routes that declare
`contracts.gatewayMethodDispatch: ["authenticated-request"]`. It is an
intentional-use guard for reviewed native plugins, not a sandbox boundary.
Hook guard semantics to keep in mind:
- `before_tool_call`: `{ block: true }` is terminal and stops lower-priority handlers.
- `before_tool_call`: `{ block: false }` is treated as no decision.
- `before_tool_call`: `{ requireApproval: { ... } }` pauses agent execution and prompts the user for approval via the exec approval overlay, native channel approval clients, or the `/approve` command on any channel.
- `before_install`: `{ block: true }` is terminal and stops lower-priority handlers.
- `before_install`: `{ block: false }` is treated as no decision.
- `message_sending`: `{ cancel: true }` is terminal and stops lower-priority handlers.
- `message_sending`: `{ cancel: false }` is treated as no decision.
- `message_received`: prefer the typed `threadId` field when you need inbound thread/topic routing. Keep `metadata` for channel-specific extras.
- `message_sending`: prefer typed `replyToId` / `threadId` routing fields over channel-specific metadata keys.
The `/approve` command handles both exec and plugin approvals with bounded fallback: when an exec approval id is not found, OpenClaw retries the same id through plugin approvals. Plugin approval forwarding can be configured independently via `approvals.plugin` in config.
If custom approval plumbing needs to detect that same bounded fallback case,
prefer `isApprovalNotFoundError` from `openclaw/plugin-sdk/error-runtime`
instead of matching approval-expiry strings manually.
See [Plugin hooks](/plugins/hooks) for examples and the hook reference.
## Registering agent tools
Tools are typed functions the LLM can call. They can be required (always
available) or optional (user opt-in):
For simple plugins that only own a fixed set of tools, prefer
[`defineToolPlugin`](/plugins/tool-plugins). It generates manifest metadata and
keeps `contracts.tools` aligned. Use the lower-level `api.registerTool(...)`
surface when the plugin also owns channels, providers, hooks, services,
commands, or fully dynamic tool registration.
```typescript
register(api) {

View File

@@ -196,6 +196,22 @@ type BeforeToolCallResult = {
timeoutMs?: number;
timeoutBehavior?: "allow" | "deny";
allowedDecisions?: Array<"allow-once" | "allow-always" | "deny">;
actions?: Array<
| {
kind: "decision";
label: string;
style: "primary" | "secondary" | "success" | "danger";
decision: "allow-once" | "allow-always" | "deny";
commandTemplate: string;
}
| {
kind: "command";
label: string;
style: "primary" | "secondary" | "success" | "danger";
commandTemplate: string;
}
>;
keepPendingWithoutRoute?: boolean;
pluginId?: string;
onResolution?: (
decision: "allow-once" | "allow-always" | "deny" | "timeout" | "cancelled",
@@ -211,6 +227,21 @@ Hook guard behavior for typed lifecycle hooks:
- `params` rewrites the tool parameters for execution.
- `requireApproval` pauses the agent run and asks the user through plugin
approvals. The `/approve` command can approve both exec and plugin approvals.
- `allowedDecisions` limits the built-in `/approve` decisions. Omit it to offer
`allow-once`, `allow-always`, and `deny`.
- `actions` customizes the commands shown with the approval. OpenClaw replaces
`{id}` in each `commandTemplate` with the generated approval id before storing
or rendering the request.
- Use `kind: "decision"` plus `decision` when the action resolves the approval
through OpenClaw's approval system. Native clients can render those actions as
buttons with canonical OpenClaw approval callbacks. Decision actions must be
included in `allowedDecisions` when that field is present. Use
`kind: "command"` for plugin-owned commands that collect more context or
verify an external workflow before resolving the approval; native clients
should leave those as visible command text.
- `keepPendingWithoutRoute: true` keeps the request pending when no approval
client or initiating-channel route can receive it. Use this only when your
plugin provides another documented command or UI path to resolve the request.
- A lower-priority `block: true` can still block after a higher-priority hook
requested approval.
- `onResolution` receives the resolved approval decision - `allow-once`,

View File

@@ -140,40 +140,40 @@ commands.
## Official external packages
| Plugin | Description | Distribution | Surface |
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
| [acpx](/plugins/reference/acpx) | OpenClaw ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`<br />npm; ClawHub | skills |
| [amazon-bedrock](/plugins/reference/amazon-bedrock) | OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support. | `@openclaw/amazon-bedrock-provider`<br />npm; ClawHub | providers: amazon-bedrock; contracts: memoryEmbeddingProviders |
| [amazon-bedrock-mantle](/plugins/reference/amazon-bedrock-mantle) | OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing. | `@openclaw/amazon-bedrock-mantle-provider`<br />npm; ClawHub | providers: amazon-bedrock-mantle |
| [anthropic-vertex](/plugins/reference/anthropic-vertex) | OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI. | `@openclaw/anthropic-vertex-provider`<br />npm; ClawHub | providers: anthropic-vertex |
| [brave](/plugins/reference/brave) | OpenClaw Brave Search provider plugin for web search. | `@openclaw/brave-plugin`<br />npm; ClawHub | contracts: webSearchProviders |
| [codex](/plugins/reference/codex) | OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog. | `@openclaw/codex`<br />npm; ClawHub | providers: codex; contracts: mediaUnderstandingProviders, migrationProviders |
| [diagnostics-otel](/plugins/reference/diagnostics-otel) | OpenClaw diagnostics OpenTelemetry exporter for metrics and traces. | `@openclaw/diagnostics-otel`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-otel` | plugin |
| [diagnostics-prometheus](/plugins/reference/diagnostics-prometheus) | OpenClaw diagnostics Prometheus exporter for runtime metrics. | `@openclaw/diagnostics-prometheus`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-prometheus` | plugin |
| [diffs](/plugins/reference/diffs) | OpenClaw read-only diff viewer plugin and file renderer for agents. | `@openclaw/diffs`<br />npm; ClawHub | contracts: tools; skills |
| [diffs-language-pack](/plugins/reference/diffs-language-pack) | Adds syntax highlighting for languages outside the default diffs viewer set. | `@openclaw/diffs-language-pack`<br />npm; ClawHub: `clawhub:@openclaw/diffs-language-pack` | plugin |
| [discord](/plugins/reference/discord) | OpenClaw Discord channel plugin for channels, DMs, commands, and app events. | `@openclaw/discord`<br />npm; ClawHub | channels: discord; contracts: transcriptSourceProviders |
| [feishu](/plugins/reference/feishu) | OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng). | `@openclaw/feishu`<br />npm; ClawHub | channels: feishu; contracts: tools; skills |
| [google-meet](/plugins/reference/google-meet) | OpenClaw Google Meet participant plugin for joining calls through Chrome or Twilio transports. | `@openclaw/google-meet`<br />npm; ClawHub | contracts: tools |
| [googlechat](/plugins/reference/googlechat) | OpenClaw Google Chat channel plugin for spaces and direct messages. | `@openclaw/googlechat`<br />npm; ClawHub | channels: googlechat |
| [line](/plugins/reference/line) | OpenClaw LINE channel plugin for LINE Bot API chats. | `@openclaw/line`<br />npm; ClawHub | channels: line |
| [lobster](/plugins/reference/lobster) | Lobster workflow tool plugin for typed pipelines and resumable approvals. | `@openclaw/lobster`<br />npm; ClawHub | contracts: tools |
| [matrix](/plugins/reference/matrix) | OpenClaw Matrix channel plugin for rooms and direct messages. | `@openclaw/matrix`<br />ClawHub: `clawhub:@openclaw/matrix`; npm | channels: matrix |
| [memory-lancedb](/plugins/reference/memory-lancedb) | OpenClaw LanceDB-backed long-term memory plugin with auto-recall, auto-capture, and vector search. | `@openclaw/memory-lancedb`<br />npm; ClawHub | contracts: tools |
| [msteams](/plugins/reference/msteams) | OpenClaw Microsoft Teams channel plugin for bot conversations. | `@openclaw/msteams`<br />npm; ClawHub | channels: msteams |
| [nextcloud-talk](/plugins/reference/nextcloud-talk) | OpenClaw Nextcloud Talk channel plugin for conversations. | `@openclaw/nextcloud-talk`<br />npm; ClawHub | channels: nextcloud-talk |
| [nostr](/plugins/reference/nostr) | OpenClaw Nostr channel plugin for NIP-04 encrypted direct messages. | `@openclaw/nostr`<br />npm; ClawHub | channels: nostr |
| [openshell](/plugins/reference/openshell) | OpenClaw sandbox backend for the NVIDIA OpenShell CLI with mirrored local workspaces and SSH command execution. | `@openclaw/openshell-sandbox`<br />npm; ClawHub | plugin |
| [pixverse](/plugins/reference/pixverse) | OpenClaw PixVerse video generation provider plugin. | `@openclaw/pixverse-provider`<br />npm; ClawHub | contracts: videoGenerationProviders |
| [qqbot](/plugins/reference/qqbot) | OpenClaw QQ Bot channel plugin for group and direct-message workflows. | `@openclaw/qqbot`<br />npm; ClawHub | channels: qqbot; contracts: tools; skills |
| [slack](/plugins/reference/slack) | OpenClaw Slack channel plugin for channels, DMs, commands, and app events. | `@openclaw/slack`<br />npm; ClawHub | channels: slack |
| [synology-chat](/plugins/reference/synology-chat) | Synology Chat channel plugin for OpenClaw channels and direct messages. | `@openclaw/synology-chat`<br />npm; ClawHub | channels: synology-chat |
| [tlon](/plugins/reference/tlon) | OpenClaw Tlon/Urbit channel plugin for chat workflows. | `@openclaw/tlon`<br />npm; ClawHub | channels: tlon; skills |
| [twitch](/plugins/reference/twitch) | OpenClaw Twitch channel plugin for chat and moderation workflows. | `@openclaw/twitch`<br />npm; ClawHub | channels: twitch |
| [voice-call](/plugins/reference/voice-call) | OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls. | `@openclaw/voice-call`<br />npm; ClawHub | contracts: tools |
| [whatsapp](/plugins/reference/whatsapp) | OpenClaw WhatsApp channel plugin for WhatsApp Web chats. | `@openclaw/whatsapp`<br />ClawHub: `clawhub:@openclaw/whatsapp`; npm | channels: whatsapp |
| [zalo](/plugins/reference/zalo) | OpenClaw Zalo channel plugin for bot and webhook chats. | `@openclaw/zalo`<br />npm; ClawHub | channels: zalo |
| [zalouser](/plugins/reference/zalouser) | OpenClaw Zalo Personal Account plugin via native zca-js integration. | `@openclaw/zalouser`<br />npm; ClawHub | channels: zalouser; contracts: tools |
| Plugin | Description | Distribution | Surface |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
| [acpx](/plugins/reference/acpx) | Embedded ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`<br />npm; ClawHub | skills |
| [amazon-bedrock](/plugins/reference/amazon-bedrock) | Adds Amazon Bedrock model provider support to OpenClaw. | `@openclaw/amazon-bedrock-provider`<br />npm; ClawHub | providers: amazon-bedrock; contracts: memoryEmbeddingProviders |
| [amazon-bedrock-mantle](/plugins/reference/amazon-bedrock-mantle) | Adds Amazon Bedrock Mantle model provider support to OpenClaw. | `@openclaw/amazon-bedrock-mantle-provider`<br />npm; ClawHub | providers: amazon-bedrock-mantle |
| [anthropic-vertex](/plugins/reference/anthropic-vertex) | Adds Anthropic Vertex model provider support to OpenClaw. | `@openclaw/anthropic-vertex-provider`<br />npm; ClawHub | providers: anthropic-vertex |
| [brave](/plugins/reference/brave) | Adds web search provider support. | `@openclaw/brave-plugin`<br />npm; ClawHub | contracts: webSearchProviders |
| [codex](/plugins/reference/codex) | Codex app-server harness and Codex-managed GPT model catalog. | `@openclaw/codex`<br />npm; ClawHub | providers: codex; contracts: mediaUnderstandingProviders, migrationProviders |
| [diagnostics-otel](/plugins/reference/diagnostics-otel) | OpenClaw diagnostics OpenTelemetry exporter. | `@openclaw/diagnostics-otel`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-otel` | plugin |
| [diagnostics-prometheus](/plugins/reference/diagnostics-prometheus) | OpenClaw diagnostics Prometheus exporter. | `@openclaw/diagnostics-prometheus`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-prometheus` | plugin |
| [diffs](/plugins/reference/diffs) | Read-only diff viewer and file renderer for agents. | `@openclaw/diffs`<br />npm; ClawHub | contracts: tools; skills |
| [diffs-language-pack](/plugins/reference/diffs-language-pack) | Adds syntax highlighting for languages outside the default diffs viewer set. | `@openclaw/diffs-language-pack`<br />npm; ClawHub: `clawhub:@openclaw/diffs-language-pack` | plugin |
| [discord](/plugins/reference/discord) | Adds the Discord channel surface for sending and receiving OpenClaw messages. | `@openclaw/discord`<br />npm; ClawHub | channels: discord; contracts: transcriptSourceProviders |
| [feishu](/plugins/reference/feishu) | Adds the Feishu channel surface for sending and receiving OpenClaw messages. | `@openclaw/feishu`<br />npm; ClawHub | channels: feishu; contracts: tools; skills |
| [google-meet](/plugins/reference/google-meet) | Join Google Meet calls through Chrome or Twilio transports. | `@openclaw/google-meet`<br />npm; ClawHub | contracts: tools |
| [googlechat](/plugins/reference/googlechat) | Adds the Google Chat channel surface for sending and receiving OpenClaw messages. | `@openclaw/googlechat`<br />npm; ClawHub | channels: googlechat |
| [line](/plugins/reference/line) | Adds the LINE channel surface for sending and receiving OpenClaw messages. | `@openclaw/line`<br />npm; ClawHub | channels: line |
| [lobster](/plugins/reference/lobster) | Typed workflow tool with resumable approvals. | `@openclaw/lobster`<br />npm; ClawHub | contracts: tools |
| [matrix](/plugins/reference/matrix) | Adds the Matrix channel surface for sending and receiving OpenClaw messages. | `@openclaw/matrix`<br />ClawHub: `clawhub:@openclaw/matrix`; npm | channels: matrix |
| [memory-lancedb](/plugins/reference/memory-lancedb) | Adds agent-callable tools. | `@openclaw/memory-lancedb`<br />npm; ClawHub | contracts: tools |
| [msteams](/plugins/reference/msteams) | Adds the Microsoft Teams channel surface for sending and receiving OpenClaw messages. | `@openclaw/msteams`<br />npm; ClawHub | channels: msteams |
| [nextcloud-talk](/plugins/reference/nextcloud-talk) | Adds the Nextcloud Talk channel surface for sending and receiving OpenClaw messages. | `@openclaw/nextcloud-talk`<br />npm; ClawHub | channels: nextcloud-talk |
| [nostr](/plugins/reference/nostr) | Adds the Nostr channel surface for sending and receiving OpenClaw messages. | `@openclaw/nostr`<br />npm; ClawHub | channels: nostr |
| [openshell](/plugins/reference/openshell) | Sandbox backend powered by the NVIDIA OpenShell CLI with mirrored local workspaces and SSH-based command execution. | `@openclaw/openshell-sandbox`<br />npm; ClawHub | plugin |
| [pixverse](/plugins/reference/pixverse) | Adds PixVerse video generation provider support to OpenClaw. | `@openclaw/pixverse-provider`<br />npm; ClawHub | contracts: videoGenerationProviders |
| [qqbot](/plugins/reference/qqbot) | Adds the QQ Bot channel surface for sending and receiving OpenClaw messages. | `@openclaw/qqbot`<br />npm; ClawHub | channels: qqbot; contracts: tools; skills |
| [slack](/plugins/reference/slack) | Adds the Slack channel surface for sending and receiving OpenClaw messages. | `@openclaw/slack`<br />npm; ClawHub | channels: slack |
| [synology-chat](/plugins/reference/synology-chat) | Adds the Synology Chat channel surface for sending and receiving OpenClaw messages. | `@openclaw/synology-chat`<br />npm; ClawHub | channels: synology-chat |
| [tlon](/plugins/reference/tlon) | Adds the Tlon channel surface for sending and receiving OpenClaw messages. | `@openclaw/tlon`<br />npm; ClawHub | channels: tlon; skills |
| [twitch](/plugins/reference/twitch) | Adds the Twitch channel surface for sending and receiving OpenClaw messages. | `@openclaw/twitch`<br />npm; ClawHub | channels: twitch |
| [voice-call](/plugins/reference/voice-call) | Adds agent-callable tools. | `@openclaw/voice-call`<br />npm; ClawHub | contracts: tools |
| [whatsapp](/plugins/reference/whatsapp) | Adds the WhatsApp channel surface for sending and receiving OpenClaw messages. | `@openclaw/whatsapp`<br />ClawHub: `clawhub:@openclaw/whatsapp`; npm | channels: whatsapp |
| [zalo](/plugins/reference/zalo) | Adds the Zalo channel surface for sending and receiving OpenClaw messages. | `@openclaw/zalo`<br />npm; ClawHub | channels: zalo |
| [zalouser](/plugins/reference/zalouser) | Adds the Zalo Personal channel surface for sending and receiving OpenClaw messages. | `@openclaw/zalouser`<br />npm; ClawHub | channels: zalouser; contracts: tools |
## Source checkout only

View File

@@ -17,17 +17,17 @@ pnpm plugins:inventory:gen
| Plugin | Description | Distribution | Surface |
| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [acpx](/plugins/reference/acpx) | OpenClaw ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`<br />npm; ClawHub | skills |
| [acpx](/plugins/reference/acpx) | Embedded ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`<br />npm; ClawHub | skills |
| [admin-http-rpc](/plugins/reference/admin-http-rpc) | OpenClaw admin HTTP RPC endpoint. | `@openclaw/admin-http-rpc`<br />included in OpenClaw | contracts: gatewayMethodDispatch |
| [alibaba](/plugins/reference/alibaba) | Adds video generation provider support. | `@openclaw/alibaba-provider`<br />included in OpenClaw | contracts: videoGenerationProviders |
| [amazon-bedrock](/plugins/reference/amazon-bedrock) | OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support. | `@openclaw/amazon-bedrock-provider`<br />npm; ClawHub | providers: amazon-bedrock; contracts: memoryEmbeddingProviders |
| [amazon-bedrock-mantle](/plugins/reference/amazon-bedrock-mantle) | OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing. | `@openclaw/amazon-bedrock-mantle-provider`<br />npm; ClawHub | providers: amazon-bedrock-mantle |
| [amazon-bedrock](/plugins/reference/amazon-bedrock) | Adds Amazon Bedrock model provider support to OpenClaw. | `@openclaw/amazon-bedrock-provider`<br />npm; ClawHub | providers: amazon-bedrock; contracts: memoryEmbeddingProviders |
| [amazon-bedrock-mantle](/plugins/reference/amazon-bedrock-mantle) | Adds Amazon Bedrock Mantle model provider support to OpenClaw. | `@openclaw/amazon-bedrock-mantle-provider`<br />npm; ClawHub | providers: amazon-bedrock-mantle |
| [anthropic](/plugins/reference/anthropic) | Adds Anthropic model provider support to OpenClaw. | `@openclaw/anthropic-provider`<br />included in OpenClaw | providers: anthropic; contracts: mediaUnderstandingProviders |
| [anthropic-vertex](/plugins/reference/anthropic-vertex) | OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI. | `@openclaw/anthropic-vertex-provider`<br />npm; ClawHub | providers: anthropic-vertex |
| [anthropic-vertex](/plugins/reference/anthropic-vertex) | Adds Anthropic Vertex model provider support to OpenClaw. | `@openclaw/anthropic-vertex-provider`<br />npm; ClawHub | providers: anthropic-vertex |
| [arcee](/plugins/reference/arcee) | Adds Arcee model provider support to OpenClaw. | `@openclaw/arcee-provider`<br />included in OpenClaw | providers: arcee |
| [azure-speech](/plugins/reference/azure-speech) | Azure AI Speech text-to-speech (MP3, native Ogg/Opus voice notes, PCM telephony). | `@openclaw/azure-speech`<br />included in OpenClaw | contracts: speechProviders |
| [bonjour](/plugins/reference/bonjour) | Advertise the local OpenClaw gateway over Bonjour/mDNS. | `@openclaw/bonjour`<br />included in OpenClaw | plugin |
| [brave](/plugins/reference/brave) | OpenClaw Brave Search provider plugin for web search. | `@openclaw/brave-plugin`<br />npm; ClawHub | contracts: webSearchProviders |
| [brave](/plugins/reference/brave) | Adds web search provider support. | `@openclaw/brave-plugin`<br />npm; ClawHub | contracts: webSearchProviders |
| [browser](/plugins/reference/browser) | Adds agent-callable tools. | `@openclaw/browser-plugin`<br />included in OpenClaw | contracts: tools; skills |
| [byteplus](/plugins/reference/byteplus) | Adds BytePlus, BytePlus Plan model provider support to OpenClaw. | `@openclaw/byteplus-provider`<br />included in OpenClaw | providers: byteplus, byteplus-plan; contracts: videoGenerationProviders |
| [canvas](/plugins/reference/canvas) | Experimental Canvas control and A2UI rendering surfaces for paired nodes. | `@openclaw/canvas-plugin`<br />included in OpenClaw | contracts: tools |
@@ -35,30 +35,30 @@ pnpm plugins:inventory:gen
| [chutes](/plugins/reference/chutes) | Adds Chutes model provider support to OpenClaw. | `@openclaw/chutes-provider`<br />included in OpenClaw | providers: chutes |
| [clickclack](/plugins/reference/clickclack) | Adds the Clickclack channel surface for sending and receiving OpenClaw messages. | `@openclaw/clickclack`<br />included in OpenClaw | channels: clickclack |
| [cloudflare-ai-gateway](/plugins/reference/cloudflare-ai-gateway) | Adds Cloudflare AI Gateway model provider support to OpenClaw. | `@openclaw/cloudflare-ai-gateway-provider`<br />included in OpenClaw | providers: cloudflare-ai-gateway |
| [codex](/plugins/reference/codex) | OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog. | `@openclaw/codex`<br />npm; ClawHub | providers: codex; contracts: mediaUnderstandingProviders, migrationProviders |
| [codex](/plugins/reference/codex) | Codex app-server harness and Codex-managed GPT model catalog. | `@openclaw/codex`<br />npm; ClawHub | providers: codex; contracts: mediaUnderstandingProviders, migrationProviders |
| [comfy](/plugins/reference/comfy) | Adds ComfyUI model provider support to OpenClaw. | `@openclaw/comfy-provider`<br />included in OpenClaw | providers: comfy; contracts: imageGenerationProviders, musicGenerationProviders, videoGenerationProviders |
| [copilot-proxy](/plugins/reference/copilot-proxy) | Adds Copilot Proxy model provider support to OpenClaw. | `@openclaw/copilot-proxy`<br />included in OpenClaw | providers: copilot-proxy |
| [deepgram](/plugins/reference/deepgram) | Adds media understanding provider support. Adds realtime transcription provider support. | `@openclaw/deepgram-provider`<br />included in OpenClaw | contracts: mediaUnderstandingProviders, realtimeTranscriptionProviders |
| [deepinfra](/plugins/reference/deepinfra) | Adds DeepInfra model provider support to OpenClaw. | `@openclaw/deepinfra-provider`<br />included in OpenClaw | providers: deepinfra; contracts: imageGenerationProviders, mediaUnderstandingProviders, memoryEmbeddingProviders, speechProviders, videoGenerationProviders |
| [deepseek](/plugins/reference/deepseek) | Adds DeepSeek model provider support to OpenClaw. | `@openclaw/deepseek-provider`<br />included in OpenClaw | providers: deepseek |
| [diagnostics-otel](/plugins/reference/diagnostics-otel) | OpenClaw diagnostics OpenTelemetry exporter for metrics and traces. | `@openclaw/diagnostics-otel`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-otel` | plugin |
| [diagnostics-prometheus](/plugins/reference/diagnostics-prometheus) | OpenClaw diagnostics Prometheus exporter for runtime metrics. | `@openclaw/diagnostics-prometheus`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-prometheus` | plugin |
| [diffs](/plugins/reference/diffs) | OpenClaw read-only diff viewer plugin and file renderer for agents. | `@openclaw/diffs`<br />npm; ClawHub | contracts: tools; skills |
| [diagnostics-otel](/plugins/reference/diagnostics-otel) | OpenClaw diagnostics OpenTelemetry exporter. | `@openclaw/diagnostics-otel`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-otel` | plugin |
| [diagnostics-prometheus](/plugins/reference/diagnostics-prometheus) | OpenClaw diagnostics Prometheus exporter. | `@openclaw/diagnostics-prometheus`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-prometheus` | plugin |
| [diffs](/plugins/reference/diffs) | Read-only diff viewer and file renderer for agents. | `@openclaw/diffs`<br />npm; ClawHub | contracts: tools; skills |
| [diffs-language-pack](/plugins/reference/diffs-language-pack) | Adds syntax highlighting for languages outside the default diffs viewer set. | `@openclaw/diffs-language-pack`<br />npm; ClawHub: `clawhub:@openclaw/diffs-language-pack` | plugin |
| [discord](/plugins/reference/discord) | OpenClaw Discord channel plugin for channels, DMs, commands, and app events. | `@openclaw/discord`<br />npm; ClawHub | channels: discord; contracts: transcriptSourceProviders |
| [discord](/plugins/reference/discord) | Adds the Discord channel surface for sending and receiving OpenClaw messages. | `@openclaw/discord`<br />npm; ClawHub | channels: discord; contracts: transcriptSourceProviders |
| [document-extract](/plugins/reference/document-extract) | Extract text and fallback page images from local document attachments. | `@openclaw/document-extract-plugin`<br />included in OpenClaw | contracts: documentExtractors |
| [duckduckgo](/plugins/reference/duckduckgo) | Adds web search provider support. | `@openclaw/duckduckgo-plugin`<br />included in OpenClaw | contracts: webSearchProviders |
| [elevenlabs](/plugins/reference/elevenlabs) | Adds media understanding provider support. Adds realtime transcription provider support. Adds text-to-speech provider support. | `@openclaw/elevenlabs-speech`<br />included in OpenClaw | contracts: mediaUnderstandingProviders, realtimeTranscriptionProviders, speechProviders |
| [exa](/plugins/reference/exa) | Adds web search provider support. | `@openclaw/exa-plugin`<br />included in OpenClaw | contracts: webSearchProviders |
| [fal](/plugins/reference/fal) | Adds fal model provider support to OpenClaw. | `@openclaw/fal-provider`<br />included in OpenClaw | providers: fal; contracts: imageGenerationProviders, musicGenerationProviders, videoGenerationProviders |
| [feishu](/plugins/reference/feishu) | OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng). | `@openclaw/feishu`<br />npm; ClawHub | channels: feishu; contracts: tools; skills |
| [feishu](/plugins/reference/feishu) | Adds the Feishu channel surface for sending and receiving OpenClaw messages. | `@openclaw/feishu`<br />npm; ClawHub | channels: feishu; contracts: tools; skills |
| [file-transfer](/plugins/reference/file-transfer) | Fetch, list, and write files on paired nodes via dedicated node commands. Bypasses bash stdout truncation by using base64 over node.invoke for binaries up to 16 MB. | `@openclaw/file-transfer`<br />included in OpenClaw | contracts: tools |
| [firecrawl](/plugins/reference/firecrawl) | Adds agent-callable tools. Adds web fetch provider support. Adds web search provider support. | `@openclaw/firecrawl-plugin`<br />included in OpenClaw | contracts: tools, webFetchProviders, webSearchProviders |
| [fireworks](/plugins/reference/fireworks) | Adds Fireworks model provider support to OpenClaw. | `@openclaw/fireworks-provider`<br />included in OpenClaw | providers: fireworks |
| [github-copilot](/plugins/reference/github-copilot) | Adds GitHub Copilot model provider support to OpenClaw. | `@openclaw/github-copilot-provider`<br />included in OpenClaw | providers: github-copilot; contracts: memoryEmbeddingProviders |
| [google](/plugins/reference/google) | Adds Google, Google Gemini CLI, Google Vertex model provider support to OpenClaw. | `@openclaw/google-plugin`<br />included in OpenClaw | providers: google, google-gemini-cli, google-vertex; contracts: imageGenerationProviders, mediaUnderstandingProviders, memoryEmbeddingProviders, musicGenerationProviders, realtimeVoiceProviders, speechProviders, videoGenerationProviders, webSearchProviders |
| [google-meet](/plugins/reference/google-meet) | OpenClaw Google Meet participant plugin for joining calls through Chrome or Twilio transports. | `@openclaw/google-meet`<br />npm; ClawHub | contracts: tools |
| [googlechat](/plugins/reference/googlechat) | OpenClaw Google Chat channel plugin for spaces and direct messages. | `@openclaw/googlechat`<br />npm; ClawHub | channels: googlechat |
| [google-meet](/plugins/reference/google-meet) | Join Google Meet calls through Chrome or Twilio transports. | `@openclaw/google-meet`<br />npm; ClawHub | contracts: tools |
| [googlechat](/plugins/reference/googlechat) | Adds the Google Chat channel surface for sending and receiving OpenClaw messages. | `@openclaw/googlechat`<br />npm; ClawHub | channels: googlechat |
| [gradium](/plugins/reference/gradium) | Adds text-to-speech provider support. | `@openclaw/gradium-speech`<br />included in OpenClaw | contracts: speechProviders |
| [groq](/plugins/reference/groq) | Adds Groq model provider support to OpenClaw. | `@openclaw/groq-provider`<br />included in OpenClaw | providers: groq; contracts: mediaUnderstandingProviders |
| [huggingface](/plugins/reference/huggingface) | Adds Hugging Face model provider support to OpenClaw. | `@openclaw/huggingface-provider`<br />included in OpenClaw | providers: huggingface |
@@ -67,15 +67,15 @@ pnpm plugins:inventory:gen
| [irc](/plugins/reference/irc) | Adds the IRC channel surface for sending and receiving OpenClaw messages. | `@openclaw/irc`<br />included in OpenClaw | channels: irc |
| [kilocode](/plugins/reference/kilocode) | Adds Kilocode model provider support to OpenClaw. | `@openclaw/kilocode-provider`<br />included in OpenClaw | providers: kilocode |
| [kimi](/plugins/reference/kimi) | Adds Kimi, Kimi Coding model provider support to OpenClaw. | `@openclaw/kimi-provider`<br />included in OpenClaw | providers: kimi, kimi-coding |
| [line](/plugins/reference/line) | OpenClaw LINE channel plugin for LINE Bot API chats. | `@openclaw/line`<br />npm; ClawHub | channels: line |
| [line](/plugins/reference/line) | Adds the LINE channel surface for sending and receiving OpenClaw messages. | `@openclaw/line`<br />npm; ClawHub | channels: line |
| [litellm](/plugins/reference/litellm) | Adds LiteLLM model provider support to OpenClaw. | `@openclaw/litellm-provider`<br />included in OpenClaw | providers: litellm; contracts: imageGenerationProviders |
| [llm-task](/plugins/reference/llm-task) | Generic JSON-only LLM tool for structured tasks callable from workflows. | `@openclaw/llm-task`<br />included in OpenClaw | contracts: tools |
| [lmstudio](/plugins/reference/lmstudio) | Adds LM Studio model provider support to OpenClaw. | `@openclaw/lmstudio-provider`<br />included in OpenClaw | providers: lmstudio; contracts: memoryEmbeddingProviders |
| [lobster](/plugins/reference/lobster) | Lobster workflow tool plugin for typed pipelines and resumable approvals. | `@openclaw/lobster`<br />npm; ClawHub | contracts: tools |
| [matrix](/plugins/reference/matrix) | OpenClaw Matrix channel plugin for rooms and direct messages. | `@openclaw/matrix`<br />ClawHub: `clawhub:@openclaw/matrix`; npm | channels: matrix |
| [lobster](/plugins/reference/lobster) | Typed workflow tool with resumable approvals. | `@openclaw/lobster`<br />npm; ClawHub | contracts: tools |
| [matrix](/plugins/reference/matrix) | Adds the Matrix channel surface for sending and receiving OpenClaw messages. | `@openclaw/matrix`<br />ClawHub: `clawhub:@openclaw/matrix`; npm | channels: matrix |
| [mattermost](/plugins/reference/mattermost) | Adds the Mattermost channel surface for sending and receiving OpenClaw messages. | `@openclaw/mattermost`<br />included in OpenClaw | channels: mattermost |
| [memory-core](/plugins/reference/memory-core) | Adds memory embedding provider support. Adds agent-callable tools. | `@openclaw/memory-core`<br />included in OpenClaw | contracts: memoryEmbeddingProviders, tools |
| [memory-lancedb](/plugins/reference/memory-lancedb) | OpenClaw LanceDB-backed long-term memory plugin with auto-recall, auto-capture, and vector search. | `@openclaw/memory-lancedb`<br />npm; ClawHub | contracts: tools |
| [memory-lancedb](/plugins/reference/memory-lancedb) | Adds agent-callable tools. | `@openclaw/memory-lancedb`<br />npm; ClawHub | contracts: tools |
| [memory-wiki](/plugins/reference/memory-wiki) | Persistent wiki compiler and Obsidian-friendly knowledge vault for OpenClaw. | `@openclaw/memory-wiki`<br />included in OpenClaw | contracts: tools; skills |
| [microsoft](/plugins/reference/microsoft) | Adds text-to-speech provider support. | `@openclaw/microsoft-speech`<br />included in OpenClaw | contracts: speechProviders |
| [microsoft-foundry](/plugins/reference/microsoft-foundry) | Adds Microsoft Foundry model provider support to OpenClaw. | `@openclaw/microsoft-foundry`<br />included in OpenClaw | providers: microsoft-foundry |
@@ -84,9 +84,9 @@ pnpm plugins:inventory:gen
| [minimax](/plugins/reference/minimax) | Adds MiniMax, MiniMax Portal model provider support to OpenClaw. | `@openclaw/minimax-provider`<br />included in OpenClaw | providers: minimax, minimax-portal; contracts: imageGenerationProviders, mediaUnderstandingProviders, musicGenerationProviders, speechProviders, videoGenerationProviders, webSearchProviders |
| [mistral](/plugins/reference/mistral) | Adds Mistral model provider support to OpenClaw. | `@openclaw/mistral-provider`<br />included in OpenClaw | providers: mistral; contracts: mediaUnderstandingProviders, memoryEmbeddingProviders, realtimeTranscriptionProviders |
| [moonshot](/plugins/reference/moonshot) | Adds Moonshot model provider support to OpenClaw. | `@openclaw/moonshot-provider`<br />included in OpenClaw | providers: moonshot; contracts: mediaUnderstandingProviders, webSearchProviders |
| [msteams](/plugins/reference/msteams) | OpenClaw Microsoft Teams channel plugin for bot conversations. | `@openclaw/msteams`<br />npm; ClawHub | channels: msteams |
| [nextcloud-talk](/plugins/reference/nextcloud-talk) | OpenClaw Nextcloud Talk channel plugin for conversations. | `@openclaw/nextcloud-talk`<br />npm; ClawHub | channels: nextcloud-talk |
| [nostr](/plugins/reference/nostr) | OpenClaw Nostr channel plugin for NIP-04 encrypted direct messages. | `@openclaw/nostr`<br />npm; ClawHub | channels: nostr |
| [msteams](/plugins/reference/msteams) | Adds the Microsoft Teams channel surface for sending and receiving OpenClaw messages. | `@openclaw/msteams`<br />npm; ClawHub | channels: msteams |
| [nextcloud-talk](/plugins/reference/nextcloud-talk) | Adds the Nextcloud Talk channel surface for sending and receiving OpenClaw messages. | `@openclaw/nextcloud-talk`<br />npm; ClawHub | channels: nextcloud-talk |
| [nostr](/plugins/reference/nostr) | Adds the Nostr channel surface for sending and receiving OpenClaw messages. | `@openclaw/nostr`<br />npm; ClawHub | channels: nostr |
| [nvidia](/plugins/reference/nvidia) | Adds NVIDIA model provider support to OpenClaw. | `@openclaw/nvidia-provider`<br />included in OpenClaw | providers: nvidia |
| [oc-path](/plugins/reference/oc-path) | Adds the openclaw path CLI for oc:// workspace file addressing. | `@openclaw/oc-path`<br />included in OpenClaw | plugin |
| [ollama](/plugins/reference/ollama) | Adds Ollama model provider support to OpenClaw. | `@openclaw/ollama-provider`<br />included in OpenClaw | providers: ollama; contracts: memoryEmbeddingProviders, webSearchProviders |
@@ -95,15 +95,15 @@ pnpm plugins:inventory:gen
| [opencode](/plugins/reference/opencode) | Adds OpenCode model provider support to OpenClaw. | `@openclaw/opencode-provider`<br />included in OpenClaw | providers: opencode; contracts: mediaUnderstandingProviders |
| [opencode-go](/plugins/reference/opencode-go) | Adds OpenCode Go model provider support to OpenClaw. | `@openclaw/opencode-go-provider`<br />included in OpenClaw | providers: opencode-go; contracts: mediaUnderstandingProviders |
| [openrouter](/plugins/reference/openrouter) | Adds OpenRouter model provider support to OpenClaw. | `@openclaw/openrouter-provider`<br />included in OpenClaw | providers: openrouter; contracts: imageGenerationProviders, mediaUnderstandingProviders, musicGenerationProviders, speechProviders, videoGenerationProviders |
| [openshell](/plugins/reference/openshell) | OpenClaw sandbox backend for the NVIDIA OpenShell CLI with mirrored local workspaces and SSH command execution. | `@openclaw/openshell-sandbox`<br />npm; ClawHub | plugin |
| [openshell](/plugins/reference/openshell) | Sandbox backend powered by the NVIDIA OpenShell CLI with mirrored local workspaces and SSH-based command execution. | `@openclaw/openshell-sandbox`<br />npm; ClawHub | plugin |
| [perplexity](/plugins/reference/perplexity) | Adds web search provider support. | `@openclaw/perplexity-plugin`<br />included in OpenClaw | contracts: webSearchProviders |
| [pixverse](/plugins/reference/pixverse) | OpenClaw PixVerse video generation provider plugin. | `@openclaw/pixverse-provider`<br />npm; ClawHub | contracts: videoGenerationProviders |
| [pixverse](/plugins/reference/pixverse) | Adds PixVerse video generation provider support to OpenClaw. | `@openclaw/pixverse-provider`<br />npm; ClawHub | contracts: videoGenerationProviders |
| [policy](/plugins/reference/policy) | Adds policy-backed doctor checks for workspace conformance. | `@openclaw/policy`<br />included in OpenClaw | plugin |
| [qa-channel](/plugins/reference/qa-channel) | Adds the QA Channel surface for sending and receiving OpenClaw messages. | `@openclaw/qa-channel`<br />source checkout only | channels: qa-channel |
| [qa-lab](/plugins/reference/qa-lab) | OpenClaw QA lab plugin with private debugger UI and scenario runner. | `@openclaw/qa-lab`<br />source checkout only | plugin |
| [qa-matrix](/plugins/reference/qa-matrix) | Matrix QA transport runner and substrate. | `@openclaw/qa-matrix`<br />source checkout only | plugin |
| [qianfan](/plugins/reference/qianfan) | Adds Qianfan model provider support to OpenClaw. | `@openclaw/qianfan-provider`<br />included in OpenClaw | providers: qianfan |
| [qqbot](/plugins/reference/qqbot) | OpenClaw QQ Bot channel plugin for group and direct-message workflows. | `@openclaw/qqbot`<br />npm; ClawHub | channels: qqbot; contracts: tools; skills |
| [qqbot](/plugins/reference/qqbot) | Adds the QQ Bot channel surface for sending and receiving OpenClaw messages. | `@openclaw/qqbot`<br />npm; ClawHub | channels: qqbot; contracts: tools; skills |
| [qwen](/plugins/reference/qwen) | Adds Qwen, Qwen Cloud, Model Studio, DashScope model provider support to OpenClaw. | `@openclaw/qwen-provider`<br />included in OpenClaw | providers: qwen, qwencloud, modelstudio, dashscope; contracts: mediaUnderstandingProviders, videoGenerationProviders |
| [runway](/plugins/reference/runway) | Adds video generation provider support. | `@openclaw/runway-provider`<br />included in OpenClaw | contracts: videoGenerationProviders |
| [searxng](/plugins/reference/searxng) | Adds web search provider support. | `@openclaw/searxng-plugin`<br />included in OpenClaw | contracts: webSearchProviders |
@@ -111,30 +111,30 @@ pnpm plugins:inventory:gen
| [sglang](/plugins/reference/sglang) | Adds SGLang model provider support to OpenClaw. | `@openclaw/sglang-provider`<br />included in OpenClaw | providers: sglang |
| [signal](/plugins/reference/signal) | Adds the Signal channel surface for sending and receiving OpenClaw messages. | `@openclaw/signal`<br />included in OpenClaw | channels: signal |
| [skill-workshop](/plugins/reference/skill-workshop) | Captures repeatable workflows as workspace skills, with pending review, safe writes, and skill prompt refresh. | `@openclaw/skill-workshop`<br />included in OpenClaw | contracts: tools |
| [slack](/plugins/reference/slack) | OpenClaw Slack channel plugin for channels, DMs, commands, and app events. | `@openclaw/slack`<br />npm; ClawHub | channels: slack |
| [slack](/plugins/reference/slack) | Adds the Slack channel surface for sending and receiving OpenClaw messages. | `@openclaw/slack`<br />npm; ClawHub | channels: slack |
| [stepfun](/plugins/reference/stepfun) | Adds StepFun, StepFun Plan model provider support to OpenClaw. | `@openclaw/stepfun-provider`<br />included in OpenClaw | providers: stepfun, stepfun-plan |
| [synology-chat](/plugins/reference/synology-chat) | Synology Chat channel plugin for OpenClaw channels and direct messages. | `@openclaw/synology-chat`<br />npm; ClawHub | channels: synology-chat |
| [synology-chat](/plugins/reference/synology-chat) | Adds the Synology Chat channel surface for sending and receiving OpenClaw messages. | `@openclaw/synology-chat`<br />npm; ClawHub | channels: synology-chat |
| [synthetic](/plugins/reference/synthetic) | Adds Synthetic model provider support to OpenClaw. | `@openclaw/synthetic-provider`<br />included in OpenClaw | providers: synthetic |
| [tavily](/plugins/reference/tavily) | Adds agent-callable tools. Adds web search provider support. | `@openclaw/tavily-plugin`<br />included in OpenClaw | contracts: tools, webSearchProviders; skills |
| [telegram](/plugins/reference/telegram) | Adds the Telegram channel surface for sending and receiving OpenClaw messages. | `@openclaw/telegram`<br />included in OpenClaw | channels: telegram |
| [tencent](/plugins/reference/tencent) | Adds Tencent TokenHub model provider support to OpenClaw. | `@openclaw/tencent-provider`<br />included in OpenClaw | providers: tencent-tokenhub |
| [tlon](/plugins/reference/tlon) | OpenClaw Tlon/Urbit channel plugin for chat workflows. | `@openclaw/tlon`<br />npm; ClawHub | channels: tlon; skills |
| [tlon](/plugins/reference/tlon) | Adds the Tlon channel surface for sending and receiving OpenClaw messages. | `@openclaw/tlon`<br />npm; ClawHub | channels: tlon; skills |
| [together](/plugins/reference/together) | Adds Together model provider support to OpenClaw. | `@openclaw/together-provider`<br />included in OpenClaw | providers: together; contracts: videoGenerationProviders |
| [tokenjuice](/plugins/reference/tokenjuice) | Compacts exec and bash tool results with tokenjuice reducers. | `@openclaw/tokenjuice`<br />included in OpenClaw | contracts: agentToolResultMiddleware |
| [tts-local-cli](/plugins/reference/tts-local-cli) | Adds text-to-speech provider support. | `@openclaw/tts-local-cli`<br />included in OpenClaw | contracts: speechProviders |
| [twitch](/plugins/reference/twitch) | OpenClaw Twitch channel plugin for chat and moderation workflows. | `@openclaw/twitch`<br />npm; ClawHub | channels: twitch |
| [twitch](/plugins/reference/twitch) | Adds the Twitch channel surface for sending and receiving OpenClaw messages. | `@openclaw/twitch`<br />npm; ClawHub | channels: twitch |
| [venice](/plugins/reference/venice) | Adds Venice model provider support to OpenClaw. | `@openclaw/venice-provider`<br />included in OpenClaw | providers: venice |
| [vercel-ai-gateway](/plugins/reference/vercel-ai-gateway) | Adds Vercel AI Gateway model provider support to OpenClaw. | `@openclaw/vercel-ai-gateway-provider`<br />included in OpenClaw | providers: vercel-ai-gateway |
| [vllm](/plugins/reference/vllm) | Adds vLLM model provider support to OpenClaw. | `@openclaw/vllm-provider`<br />included in OpenClaw | providers: vllm |
| [voice-call](/plugins/reference/voice-call) | OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls. | `@openclaw/voice-call`<br />npm; ClawHub | contracts: tools |
| [voice-call](/plugins/reference/voice-call) | Adds agent-callable tools. | `@openclaw/voice-call`<br />npm; ClawHub | contracts: tools |
| [volcengine](/plugins/reference/volcengine) | Adds Volcengine, Volcengine Plan model provider support to OpenClaw. | `@openclaw/volcengine-provider`<br />included in OpenClaw | providers: volcengine, volcengine-plan; contracts: speechProviders |
| [voyage](/plugins/reference/voyage) | Adds memory embedding provider support. | `@openclaw/voyage-provider`<br />included in OpenClaw | contracts: memoryEmbeddingProviders |
| [vydra](/plugins/reference/vydra) | Adds Vydra model provider support to OpenClaw. | `@openclaw/vydra-provider`<br />included in OpenClaw | providers: vydra; contracts: imageGenerationProviders, speechProviders, videoGenerationProviders |
| [web-readability](/plugins/reference/web-readability) | Extract readable article content from local HTML web fetch responses. | `@openclaw/web-readability-plugin`<br />included in OpenClaw | contracts: webContentExtractors |
| [webhooks](/plugins/reference/webhooks) | Authenticated inbound webhooks that bind external automation to OpenClaw TaskFlows. | `@openclaw/webhooks`<br />included in OpenClaw | plugin |
| [whatsapp](/plugins/reference/whatsapp) | OpenClaw WhatsApp channel plugin for WhatsApp Web chats. | `@openclaw/whatsapp`<br />ClawHub: `clawhub:@openclaw/whatsapp`; npm | channels: whatsapp |
| [whatsapp](/plugins/reference/whatsapp) | Adds the WhatsApp channel surface for sending and receiving OpenClaw messages. | `@openclaw/whatsapp`<br />ClawHub: `clawhub:@openclaw/whatsapp`; npm | channels: whatsapp |
| [xai](/plugins/reference/xai) | Adds xAI model provider support to OpenClaw. | `@openclaw/xai-plugin`<br />included in OpenClaw | providers: xai; contracts: imageGenerationProviders, mediaUnderstandingProviders, realtimeTranscriptionProviders, speechProviders, tools, videoGenerationProviders, webSearchProviders |
| [xiaomi](/plugins/reference/xiaomi) | Adds Xiaomi model provider support to OpenClaw. | `@openclaw/xiaomi-provider`<br />included in OpenClaw | providers: xiaomi; contracts: speechProviders |
| [zai](/plugins/reference/zai) | Adds Z.AI model provider support to OpenClaw. | `@openclaw/zai-provider`<br />included in OpenClaw | providers: zai; contracts: mediaUnderstandingProviders |
| [zalo](/plugins/reference/zalo) | OpenClaw Zalo channel plugin for bot and webhook chats. | `@openclaw/zalo`<br />npm; ClawHub | channels: zalo |
| [zalouser](/plugins/reference/zalouser) | OpenClaw Zalo Personal Account plugin via native zca-js integration. | `@openclaw/zalouser`<br />npm; ClawHub | channels: zalouser; contracts: tools |
| [zalo](/plugins/reference/zalo) | Adds the Zalo channel surface for sending and receiving OpenClaw messages. | `@openclaw/zalo`<br />npm; ClawHub | channels: zalo |
| [zalouser](/plugins/reference/zalouser) | Adds the Zalo Personal channel surface for sending and receiving OpenClaw messages. | `@openclaw/zalouser`<br />npm; ClawHub | channels: zalouser; contracts: tools |

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw ACP runtime backend with plugin-owned session and transport management."
summary: "Embedded ACP runtime backend with plugin-owned session and transport management."
read_when:
- You are installing, configuring, or auditing the acpx plugin
title: "ACPx plugin"
@@ -7,7 +7,7 @@ title: "ACPx plugin"
# ACPx plugin
OpenClaw ACP runtime backend with plugin-owned session and transport management.
Embedded ACP runtime backend with plugin-owned session and transport management.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing."
summary: "Adds Amazon Bedrock Mantle model provider support to OpenClaw."
read_when:
- You are installing, configuring, or auditing the amazon-bedrock-mantle plugin
title: "Amazon Bedrock Mantle plugin"
@@ -7,7 +7,7 @@ title: "Amazon Bedrock Mantle plugin"
# Amazon Bedrock Mantle plugin
OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing.
Adds Amazon Bedrock Mantle model provider support to OpenClaw.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support."
summary: "Adds Amazon Bedrock model provider support to OpenClaw."
read_when:
- You are installing, configuring, or auditing the amazon-bedrock plugin
title: "Amazon Bedrock plugin"
@@ -7,7 +7,7 @@ title: "Amazon Bedrock plugin"
# Amazon Bedrock plugin
OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support.
Adds Amazon Bedrock model provider support to OpenClaw.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI."
summary: "Adds Anthropic Vertex model provider support to OpenClaw."
read_when:
- You are installing, configuring, or auditing the anthropic-vertex plugin
title: "Anthropic Vertex plugin"
@@ -7,7 +7,7 @@ title: "Anthropic Vertex plugin"
# Anthropic Vertex plugin
OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI.
Adds Anthropic Vertex model provider support to OpenClaw.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Brave Search provider plugin for web search."
summary: "Adds web search provider support."
read_when:
- You are installing, configuring, or auditing the brave plugin
title: "Brave plugin"
@@ -7,7 +7,7 @@ title: "Brave plugin"
# Brave plugin
OpenClaw Brave Search provider plugin for web search.
Adds web search provider support.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog."
summary: "Codex app-server harness and Codex-managed GPT model catalog."
read_when:
- You are installing, configuring, or auditing the codex plugin
title: "Codex plugin"
@@ -7,7 +7,7 @@ title: "Codex plugin"
# Codex plugin
OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog.
Codex app-server harness and Codex-managed GPT model catalog.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw diagnostics OpenTelemetry exporter for metrics and traces."
summary: "OpenClaw diagnostics OpenTelemetry exporter."
read_when:
- You are installing, configuring, or auditing the diagnostics-otel plugin
title: "Diagnostics OpenTelemetry plugin"
@@ -7,7 +7,7 @@ title: "Diagnostics OpenTelemetry plugin"
# Diagnostics OpenTelemetry plugin
OpenClaw diagnostics OpenTelemetry exporter for metrics and traces.
OpenClaw diagnostics OpenTelemetry exporter.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw diagnostics Prometheus exporter for runtime metrics."
summary: "OpenClaw diagnostics Prometheus exporter."
read_when:
- You are installing, configuring, or auditing the diagnostics-prometheus plugin
title: "Diagnostics Prometheus plugin"
@@ -7,7 +7,7 @@ title: "Diagnostics Prometheus plugin"
# Diagnostics Prometheus plugin
OpenClaw diagnostics Prometheus exporter for runtime metrics.
OpenClaw diagnostics Prometheus exporter.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw read-only diff viewer plugin and file renderer for agents."
summary: "Read-only diff viewer and file renderer for agents."
read_when:
- You are installing, configuring, or auditing the diffs plugin
title: "Diffs plugin"
@@ -7,7 +7,7 @@ title: "Diffs plugin"
# Diffs plugin
OpenClaw read-only diff viewer plugin and file renderer for agents.
Read-only diff viewer and file renderer for agents.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Discord channel plugin for channels, DMs, commands, and app events."
summary: "Adds the Discord channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the discord plugin
title: "Discord plugin"
@@ -7,7 +7,7 @@ title: "Discord plugin"
# Discord plugin
OpenClaw Discord channel plugin for channels, DMs, commands, and app events.
Adds the Discord channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng)."
summary: "Adds the Feishu channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the feishu plugin
title: "Feishu plugin"
@@ -7,7 +7,7 @@ title: "Feishu plugin"
# Feishu plugin
OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng).
Adds the Feishu channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Google Meet participant plugin for joining calls through Chrome or Twilio transports."
summary: "Join Google Meet calls through Chrome or Twilio transports."
read_when:
- You are installing, configuring, or auditing the google-meet plugin
title: "Google Meet plugin"
@@ -7,7 +7,7 @@ title: "Google Meet plugin"
# Google Meet plugin
OpenClaw Google Meet participant plugin for joining calls through Chrome or Twilio transports.
Join Google Meet calls through Chrome or Twilio transports.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Google Chat channel plugin for spaces and direct messages."
summary: "Adds the Google Chat channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the googlechat plugin
title: "Google Chat plugin"
@@ -7,7 +7,7 @@ title: "Google Chat plugin"
# Google Chat plugin
OpenClaw Google Chat channel plugin for spaces and direct messages.
Adds the Google Chat channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw LINE channel plugin for LINE Bot API chats."
summary: "Adds the LINE channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the line plugin
title: "LINE plugin"
@@ -7,7 +7,7 @@ title: "LINE plugin"
# LINE plugin
OpenClaw LINE channel plugin for LINE Bot API chats.
Adds the LINE channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "Lobster workflow tool plugin for typed pipelines and resumable approvals."
summary: "Typed workflow tool with resumable approvals."
read_when:
- You are installing, configuring, or auditing the lobster plugin
title: "Lobster plugin"
@@ -7,7 +7,7 @@ title: "Lobster plugin"
# Lobster plugin
Lobster workflow tool plugin for typed pipelines and resumable approvals.
Typed workflow tool with resumable approvals.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Matrix channel plugin for rooms and direct messages."
summary: "Adds the Matrix channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the matrix plugin
title: "Matrix plugin"
@@ -7,7 +7,7 @@ title: "Matrix plugin"
# Matrix plugin
OpenClaw Matrix channel plugin for rooms and direct messages.
Adds the Matrix channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw LanceDB-backed long-term memory plugin with auto-recall, auto-capture, and vector search."
summary: "Adds agent-callable tools."
read_when:
- You are installing, configuring, or auditing the memory-lancedb plugin
title: "Memory Lancedb plugin"
@@ -7,7 +7,7 @@ title: "Memory Lancedb plugin"
# Memory Lancedb plugin
OpenClaw LanceDB-backed long-term memory plugin with auto-recall, auto-capture, and vector search.
Adds agent-callable tools.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Microsoft Teams channel plugin for bot conversations."
summary: "Adds the Microsoft Teams channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the msteams plugin
title: "Microsoft Teams plugin"
@@ -7,7 +7,7 @@ title: "Microsoft Teams plugin"
# Microsoft Teams plugin
OpenClaw Microsoft Teams channel plugin for bot conversations.
Adds the Microsoft Teams channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Nextcloud Talk channel plugin for conversations."
summary: "Adds the Nextcloud Talk channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the nextcloud-talk plugin
title: "Nextcloud Talk plugin"
@@ -7,7 +7,7 @@ title: "Nextcloud Talk plugin"
# Nextcloud Talk plugin
OpenClaw Nextcloud Talk channel plugin for conversations.
Adds the Nextcloud Talk channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Nostr channel plugin for NIP-04 encrypted direct messages."
summary: "Adds the Nostr channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the nostr plugin
title: "Nostr plugin"
@@ -7,7 +7,7 @@ title: "Nostr plugin"
# Nostr plugin
OpenClaw Nostr channel plugin for NIP-04 encrypted direct messages.
Adds the Nostr channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw sandbox backend for the NVIDIA OpenShell CLI with mirrored local workspaces and SSH command execution."
summary: "Sandbox backend powered by the NVIDIA OpenShell CLI with mirrored local workspaces and SSH-based command execution."
read_when:
- You are installing, configuring, or auditing the openshell plugin
title: "Openshell plugin"
@@ -7,7 +7,7 @@ title: "Openshell plugin"
# Openshell plugin
OpenClaw sandbox backend for the NVIDIA OpenShell CLI with mirrored local workspaces and SSH command execution.
Sandbox backend powered by the NVIDIA OpenShell CLI with mirrored local workspaces and SSH-based command execution.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw PixVerse video generation provider plugin."
summary: "Adds PixVerse video generation provider support to OpenClaw."
read_when:
- You are installing, configuring, or auditing the pixverse plugin
title: "PixVerse plugin"
@@ -7,7 +7,7 @@ title: "PixVerse plugin"
# PixVerse plugin
OpenClaw PixVerse video generation provider plugin.
Adds PixVerse video generation provider support to OpenClaw.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw QQ Bot channel plugin for group and direct-message workflows."
summary: "Adds the QQ Bot channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the qqbot plugin
title: "QQ Bot plugin"
@@ -7,7 +7,7 @@ title: "QQ Bot plugin"
# QQ Bot plugin
OpenClaw QQ Bot channel plugin for group and direct-message workflows.
Adds the QQ Bot channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Slack channel plugin for channels, DMs, commands, and app events."
summary: "Adds the Slack channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the slack plugin
title: "Slack plugin"
@@ -7,7 +7,7 @@ title: "Slack plugin"
# Slack plugin
OpenClaw Slack channel plugin for channels, DMs, commands, and app events.
Adds the Slack channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "Synology Chat channel plugin for OpenClaw channels and direct messages."
summary: "Adds the Synology Chat channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the synology-chat plugin
title: "Synology Chat plugin"
@@ -7,7 +7,7 @@ title: "Synology Chat plugin"
# Synology Chat plugin
Synology Chat channel plugin for OpenClaw channels and direct messages.
Adds the Synology Chat channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Tlon/Urbit channel plugin for chat workflows."
summary: "Adds the Tlon channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the tlon plugin
title: "Tlon plugin"
@@ -7,7 +7,7 @@ title: "Tlon plugin"
# Tlon plugin
OpenClaw Tlon/Urbit channel plugin for chat workflows.
Adds the Tlon channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Twitch channel plugin for chat and moderation workflows."
summary: "Adds the Twitch channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the twitch plugin
title: "Twitch plugin"
@@ -7,7 +7,7 @@ title: "Twitch plugin"
# Twitch plugin
OpenClaw Twitch channel plugin for chat and moderation workflows.
Adds the Twitch channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls."
summary: "Adds agent-callable tools."
read_when:
- You are installing, configuring, or auditing the voice-call plugin
title: "Voice Call plugin"
@@ -7,7 +7,7 @@ title: "Voice Call plugin"
# Voice Call plugin
OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls.
Adds agent-callable tools.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw WhatsApp channel plugin for WhatsApp Web chats."
summary: "Adds the WhatsApp channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the whatsapp plugin
title: "WhatsApp plugin"
@@ -7,7 +7,7 @@ title: "WhatsApp plugin"
# WhatsApp plugin
OpenClaw WhatsApp channel plugin for WhatsApp Web chats.
Adds the WhatsApp channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Zalo channel plugin for bot and webhook chats."
summary: "Adds the Zalo channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the zalo plugin
title: "Zalo plugin"
@@ -7,7 +7,7 @@ title: "Zalo plugin"
# Zalo plugin
OpenClaw Zalo channel plugin for bot and webhook chats.
Adds the Zalo channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -1,5 +1,5 @@
---
summary: "OpenClaw Zalo Personal Account plugin via native zca-js integration."
summary: "Adds the Zalo Personal channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the zalouser plugin
title: "Zalo Personal plugin"
@@ -7,7 +7,7 @@ title: "Zalo Personal plugin"
# Zalo Personal plugin
OpenClaw Zalo Personal Account plugin via native zca-js integration.
Adds the Zalo Personal channel surface for sending and receiving OpenClaw messages.
## Distribution

View File

@@ -155,6 +155,7 @@ Most channel plugins do not need approval-specific code.
- If custom approval auth intentionally allows only same-chat fallback, return `markImplicitSameChatApprovalAuthorization({ authorized: true })` from `openclaw/plugin-sdk/approval-auth-runtime`; otherwise core treats the result as explicit approver authorization.
- If a channel-owned native callback resolves approvals directly, use `isImplicitSameChatApprovalAuthorization(...)` before resolving so implicit fallback still goes through the channel's normal actor authorization.
- 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:
- Use `createNativeApprovalChannelRouteGates` from `openclaw/plugin-sdk/approval-native-runtime` when a channel supports both session-origin native delivery and explicit approval forwarding targets. The helper centralizes approval config selection, `mode` handling, agent/session filters, account binding, session-target matching, and target-list matching while callers still own the channel id, default forwarding mode, account lookup, transport-enabled check, target normalization, and turn-source target resolution. Do not use it to create core-owned channel policy defaults; pass the channel's documented default mode explicitly.
- `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

View File

@@ -179,7 +179,7 @@ and pairing-path families.
| `plugin-sdk/approval-gateway-runtime` | Shared approval gateway-resolution helper |
| `plugin-sdk/approval-handler-adapter-runtime` | Lightweight native approval adapter loading helpers for hot channel entrypoints |
| `plugin-sdk/approval-handler-runtime` | Broader approval handler runtime helpers; prefer the narrower adapter/gateway seams when they are enough |
| `plugin-sdk/approval-native-runtime` | Native approval target + account-binding helpers and local native exec prompt suppression |
| `plugin-sdk/approval-native-runtime` | Native approval target, account-binding, route-gate, forwarding fallback, and local native exec prompt suppression helpers |
| `plugin-sdk/approval-reaction-runtime` | Hardcoded approval reaction bindings, reaction prompt payloads, reaction target stores, and compatibility export for local native exec prompt suppression |
| `plugin-sdk/approval-reply-runtime` | Exec/plugin approval reply payload helpers |
| `plugin-sdk/approval-runtime` | Exec/plugin approval payload helpers, native approval routing/runtime helpers, and structured approval display helpers such as `formatApprovalDisplayPath` |

View File

@@ -186,9 +186,6 @@ Per-agent overrides use `agents.list[].subagents.delegationMode`.
<ParamField path="agentId" type="string">
Spawn under another configured agent id when allowed by `subagents.allowAgents`.
</ParamField>
<ParamField path="cwd" type="string">
Optional task working directory for the child run. Native sub-agents still load bootstrap files from the target agent workspace; `cwd` only changes where runtime tools and CLI harnesses do the delegated work.
</ParamField>
<ParamField path="runtime" type='"subagent" | "acp"' default="subagent">
`acp` is only for external ACP harnesses (`claude`, `droid`, `gemini`, `opencode`, or explicitly requested Codex ACP/acpx) and for `agents.list[]` entries whose `runtime.type` is `acp`.
</ParamField>

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/acpx",
"version": "2026.5.28",
"version": "2026.5.27",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/acpx",
"version": "2026.5.28",
"version": "2026.5.27",
"dependencies": {
"@agentclientprotocol/claude-agent-acp": "0.37.0",
"@zed-industries/codex-acp": "0.15.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/acpx",
"version": "2026.5.28",
"version": "2026.5.27",
"description": "OpenClaw ACP runtime backend with plugin-owned session and transport management.",
"repository": {
"type": "git",
@@ -26,10 +26,10 @@
"minHostVersion": ">=2026.4.25"
},
"compat": {
"pluginApi": ">=2026.5.28"
"pluginApi": ">=2026.5.27"
},
"build": {
"openclawVersion": "2026.5.28",
"openclawVersion": "2026.5.27",
"staticAssets": [
{
"source": "./src/runtime-internals/mcp-proxy.mjs",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/admin-http-rpc",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw admin HTTP RPC endpoint",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/alibaba-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw Alibaba Model Studio video provider plugin",
"type": "module",

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/amazon-bedrock-mantle-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/amazon-bedrock-mantle-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"dependencies": {
"@anthropic-ai/sdk": "0.98.0",
"@aws/bedrock-token-generator": "1.1.0"

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/amazon-bedrock-mantle-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"description": "OpenClaw Amazon Bedrock Mantle provider plugin for OpenAI-compatible model routing.",
"repository": {
"type": "git",
@@ -24,10 +24,10 @@
"minHostVersion": ">=2026.5.12-beta.1"
},
"compat": {
"pluginApi": ">=2026.5.28"
"pluginApi": ">=2026.5.27"
},
"build": {
"openclawVersion": "2026.5.28",
"openclawVersion": "2026.5.27",
"bundledDist": false
},
"release": {

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/amazon-bedrock-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/amazon-bedrock-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"dependencies": {
"@aws-sdk/client-bedrock": "3.1053.0",
"@aws-sdk/client-bedrock-runtime": "3.1053.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/amazon-bedrock-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"description": "OpenClaw Amazon Bedrock provider plugin with model discovery, embeddings, and guardrail support.",
"repository": {
"type": "git",
@@ -28,10 +28,10 @@
"minHostVersion": ">=2026.5.12-beta.1"
},
"compat": {
"pluginApi": ">=2026.5.28"
"pluginApi": ">=2026.5.27"
},
"build": {
"openclawVersion": "2026.5.28",
"openclawVersion": "2026.5.27",
"bundledDist": false
},
"release": {

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/anthropic-vertex-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/anthropic-vertex-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"dependencies": {
"@anthropic-ai/vertex-sdk": "0.16.1"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/anthropic-vertex-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"description": "OpenClaw Anthropic Vertex provider plugin for Claude models on Google Vertex AI.",
"repository": {
"type": "git",
@@ -23,10 +23,10 @@
"minHostVersion": ">=2026.5.12-beta.1"
},
"compat": {
"pluginApi": ">=2026.5.28"
"pluginApi": ">=2026.5.27"
},
"build": {
"openclawVersion": "2026.5.28",
"openclawVersion": "2026.5.27",
"bundledDist": false
},
"release": {

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/anthropic-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw Anthropic provider plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/arcee-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw Arcee provider plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/azure-speech",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw Azure Speech plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/bonjour",
"version": "2026.5.28",
"version": "2026.5.27",
"description": "OpenClaw Bonjour/mDNS gateway discovery",
"type": "module",
"dependencies": {

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/brave-plugin",
"version": "2026.5.28",
"version": "2026.5.27",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/brave-plugin",
"version": "2026.5.28"
"version": "2026.5.27"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/brave-plugin",
"version": "2026.5.28",
"version": "2026.5.27",
"description": "OpenClaw Brave Search provider plugin for web search.",
"repository": {
"type": "git",
@@ -21,10 +21,10 @@
"allowInvalidConfigRecovery": true
},
"compat": {
"pluginApi": ">=2026.5.28"
"pluginApi": ">=2026.5.27"
},
"build": {
"openclawVersion": "2026.5.28"
"openclawVersion": "2026.5.27"
},
"release": {
"publishToClawHub": true,

View File

@@ -1,51 +0,0 @@
import { describe, expect, it, vi } from "vitest";
import { createBraveWebSearchProvider } from "./brave-web-search-provider.js";
const runtimeMock = vi.hoisted(() => {
const searchConfigs: Array<Record<string, unknown> | undefined> = [];
return {
searchConfigs,
executeBraveSearch: vi.fn(async (_args: unknown, searchConfig?: Record<string, unknown>) => {
searchConfigs.push(searchConfig);
return { results: [] };
}),
};
});
vi.mock("./brave-web-search-provider.runtime.js", () => ({
executeBraveSearch: runtimeMock.executeBraveSearch,
}));
describe("brave web search config merge", () => {
it("keeps plugin webSearch runtime-only after merging it for the tool", async () => {
const provider = createBraveWebSearchProvider();
const tool = provider.createTool({
config: {
plugins: {
entries: {
brave: {
config: {
webSearch: {
apiKey: "brave-test-key",
mode: "llm-context",
},
},
},
},
},
},
searchConfig: { provider: "brave" },
});
await tool?.execute({ query: "OpenClaw docs" });
const [searchConfig] = runtimeMock.searchConfigs;
expect(searchConfig?.brave).toEqual({
apiKey: "brave-test-key",
mode: "llm-context",
});
expect(searchConfig?.apiKey).toBe("brave-test-key");
expect(Object.keys(searchConfig ?? {})).toEqual(["provider", "apiKey"]);
expect(Object.getOwnPropertyDescriptor(searchConfig ?? {}, "brave")?.enumerable).toBe(false);
});
});

View File

@@ -1,15 +1,10 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-runtime";
import type {
SearchConfigRecord,
WebSearchProviderPlugin,
WebSearchProviderToolDefinition,
} from "openclaw/plugin-sdk/provider-web-search";
import {
createWebSearchProviderContractFields,
mergeScopedSearchConfig,
resolveProviderWebSearchPluginConfig,
} from "openclaw/plugin-sdk/provider-web-search-config-contract";
import { createWebSearchProviderContractFields } from "openclaw/plugin-sdk/provider-web-search-config-contract";
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
const BRAVE_CREDENTIAL_PATH = "plugins.entries.brave.config.webSearch.apiKey";
@@ -67,8 +62,22 @@ const BraveSearchSchema = {
},
} satisfies Record<string, unknown>;
function resolveProviderWebSearchPluginConfig(
config: unknown,
pluginId: string,
): Record<string, unknown> | undefined {
if (!isRecord(config)) {
return undefined;
}
const plugins = isRecord(config.plugins) ? config.plugins : undefined;
const entries = isRecord(plugins?.entries) ? plugins.entries : undefined;
const entry = isRecord(entries?.[pluginId]) ? entries[pluginId] : undefined;
const pluginConfig = isRecord(entry?.config) ? entry.config : undefined;
return isRecord(pluginConfig?.webSearch) ? pluginConfig.webSearch : undefined;
}
function resolveLegacyTopLevelBraveCredential(
config: OpenClawConfig | undefined,
config: unknown,
): { path: string; value: unknown } | undefined {
if (!isRecord(config)) {
return undefined;
@@ -82,13 +91,39 @@ function resolveLegacyTopLevelBraveCredential(
return { path: "tools.web.search.apiKey", value: search.apiKey };
}
function resolveConfiguredBraveCredential(config: OpenClawConfig | undefined): unknown {
function resolveConfiguredBraveCredential(config: unknown): unknown {
return (
resolveProviderWebSearchPluginConfig(config, "brave")?.apiKey ??
resolveLegacyTopLevelBraveCredential(config)?.value
);
}
function mergeScopedSearchConfig(
searchConfig: Record<string, unknown> | undefined,
key: string,
pluginConfig: Record<string, unknown> | undefined,
options?: { mirrorApiKeyToTopLevel?: boolean },
): Record<string, unknown> | undefined {
if (!pluginConfig) {
return searchConfig;
}
const currentScoped = isRecord(searchConfig?.[key]) ? searchConfig?.[key] : {};
const next: Record<string, unknown> = {
...searchConfig,
[key]: {
...currentScoped,
...pluginConfig,
},
};
if (options?.mirrorApiKeyToTopLevel && pluginConfig.apiKey !== undefined) {
next.apiKey = pluginConfig.apiKey;
}
return next;
}
function resolveBraveMode(searchConfig?: Record<string, unknown>): "web" | "llm-context" {
const brave = isRecord(searchConfig?.brave) ? searchConfig.brave : undefined;
return brave?.mode === "llm-context" ? "llm-context" : "web";

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/browser-plugin",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw browser tool plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/byteplus-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw BytePlus provider plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/canvas-plugin",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw Canvas plugin",
"type": "module",

View File

@@ -8,10 +8,6 @@ import {
resolveNodeFromNodeList,
type NodeMatchCandidate,
} from "openclaw/plugin-sdk/gateway-runtime";
import {
parseStrictFiniteNumber,
parseStrictPositiveInteger,
} from "openclaw/plugin-sdk/number-runtime";
import { defaultRuntime } from "openclaw/plugin-sdk/runtime";
import {
normalizeLowercaseStringOrEmpty,
@@ -74,6 +70,41 @@ export type CanvasCliDependencies = {
type CanvasNodeCandidate = NodeMatchCandidate;
type CanvasSnapshotRequestFormat = "png" | "jpeg";
function normalizeNumericString(value: string): string | undefined {
const trimmed = value.trim();
return trimmed ? trimmed : undefined;
}
function parseStrictPositiveInteger(value: unknown): number | undefined {
if (typeof value === "number") {
return Number.isSafeInteger(value) && value > 0 ? value : undefined;
}
if (typeof value !== "string") {
return undefined;
}
const normalized = normalizeNumericString(value);
if (!normalized || !/^\+?\d+$/.test(normalized)) {
return undefined;
}
const parsed = Number(normalized);
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined;
}
function parseStrictFiniteNumber(value: unknown): number | undefined {
if (typeof value === "number") {
return Number.isFinite(value) ? value : undefined;
}
if (typeof value !== "string") {
return undefined;
}
const normalized = normalizeNumericString(value);
if (!normalized || !/^[+-]?(?:(?:\d+\.?\d*)|(?:\.\d+))(?:e[+-]?\d+)?$/i.test(normalized)) {
return undefined;
}
const parsed = Number(normalized);
return Number.isFinite(parsed) ? parsed : undefined;
}
function parseCanvasSnapshotRequestFormat(raw: unknown): CanvasSnapshotRequestFormat {
const format = normalizeLowercaseStringOrEmpty(normalizeOptionalString(raw) ?? "jpg");
switch (format) {

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/cerebras-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw Cerebras provider plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/chutes-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw Chutes.ai provider plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/clickclack",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw ClickClack channel plugin",
"type": "module",
@@ -18,7 +18,7 @@
"openclaw": "workspace:*"
},
"peerDependencies": {
"openclaw": ">=2026.5.28"
"openclaw": ">=2026.5.27"
},
"peerDependenciesMeta": {
"openclaw": {

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/cloudflare-ai-gateway-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw Cloudflare AI Gateway provider plugin",
"type": "module",

View File

@@ -1,12 +1,12 @@
{
"name": "@openclaw/codex",
"version": "2026.5.28",
"version": "2026.5.27",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@openclaw/codex",
"version": "2026.5.28",
"version": "2026.5.27",
"dependencies": {
"@openai/codex": "0.134.0",
"typebox": "1.1.38",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/codex",
"version": "2026.5.28",
"version": "2026.5.27",
"description": "OpenClaw Codex app-server harness and model provider plugin with a Codex-managed GPT catalog.",
"repository": {
"type": "git",
@@ -26,10 +26,10 @@
"minHostVersion": ">=2026.5.1-beta.1"
},
"compat": {
"pluginApi": ">=2026.5.28"
"pluginApi": ">=2026.5.27"
},
"build": {
"openclawVersion": "2026.5.28"
"openclawVersion": "2026.5.27"
},
"release": {
"publishToClawHub": true,

View File

@@ -1,202 +0,0 @@
import type {
CodexBundleMcpThreadConfig,
EmbeddedRunAttemptParams,
} from "openclaw/plugin-sdk/agent-harness-runtime";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { startCodexAttemptThread } from "./attempt-startup.js";
import { defaultLeasedCodexAppServerClientFactory } from "./client-factory.js";
import { CodexAppServerClient } from "./client.js";
import { type CodexPluginConfig, resolveCodexAppServerRuntimeOptions } from "./config.js";
import { clearSharedCodexAppServerClient } from "./shared-client.js";
import { createClientHarness, createCodexTestModel } from "./test-support.js";
type ClientHarness = ReturnType<typeof createClientHarness>;
function createAttemptParams(): EmbeddedRunAttemptParams {
return {
prompt: "hello",
sessionId: "session-1",
sessionKey: "agent:main:session-1",
sessionFile: "/tmp/session.jsonl",
effectiveCwd: "/tmp",
workspaceDir: "/tmp",
runId: "run-1",
provider: "codex",
modelId: "gpt-5.4-codex",
model: createCodexTestModel("codex"),
thinkLevel: "medium",
disableTools: true,
timeoutMs: 5_000,
authStorage: {} as never,
authProfileStore: { version: 1, profiles: {} },
modelRegistry: {} as never,
} as EmbeddedRunAttemptParams;
}
const pluginConfig: CodexPluginConfig = {
appServer: { command: "codex" },
};
const bundleMcpThreadConfig = {
configPatch: undefined,
diagnostics: [],
evaluated: false,
fingerprint: undefined,
} satisfies CodexBundleMcpThreadConfig;
function readHarnessMessages(writes: string[]): Array<{ id?: number; method?: string }> {
return writes.map((write) => JSON.parse(write) as { id?: number; method?: string });
}
function startThreadWithHarness(
startupTimeoutMs: number,
signal = new AbortController().signal,
overrides?: { pluginConfig?: CodexPluginConfig },
) {
const harness = createClientHarness();
vi.spyOn(CodexAppServerClient, "start").mockReturnValue(harness.client);
const effectivePluginConfig = overrides?.pluginConfig ?? pluginConfig;
const run = startCodexAttemptThread({
attemptClientFactory: defaultLeasedCodexAppServerClientFactory,
appServer: resolveCodexAppServerRuntimeOptions({ pluginConfig: effectivePluginConfig }),
pluginConfig: effectivePluginConfig,
computerUseConfig: effectivePluginConfig.computerUse ?? { enabled: false },
startupAuthProfileId: undefined,
startupAuthAccountCacheKey: undefined,
startupEnvApiKeyCacheKey: undefined,
agentDir: "/tmp/agent",
config: undefined,
buildAttemptParams: createAttemptParams,
sessionAgentId: "agent-1",
effectiveWorkspace: "/tmp",
effectiveCwd: "/tmp",
dynamicTools: [],
developerInstructions: undefined,
finalConfigPatch: undefined,
bundleMcpThreadConfig,
nativeToolSurfaceEnabled: true,
sandboxExecServerEnabled: false,
sandbox: null,
contextEngineProjection: undefined,
startupTimeoutMs,
signal,
onStartupTimeout: vi.fn(),
spawnedBy: undefined,
});
return { harness, run };
}
async function answerInitialize(harness: ClientHarness): Promise<void> {
await vi.waitFor(() => expect(harness.writes.length).toBeGreaterThanOrEqual(1), {
interval: 1,
timeout: 5_000,
});
const initialize = JSON.parse(harness.writes[0] ?? "{}") as { id?: number };
harness.send({ id: initialize.id, result: { userAgent: "openclaw/0.125.0 (macOS; test)" } });
}
async function waitForRequest(
harness: ClientHarness,
method: string,
): Promise<{ id?: number; method?: string }> {
await vi.waitFor(
() =>
expect(readHarnessMessages(harness.writes).some((write) => write.method === method)).toBe(
true,
),
{ interval: 1, timeout: 5_000 },
);
const request = readHarnessMessages(harness.writes).find((write) => write.method === method);
if (!request) {
throw new Error(`${method} request was not written`);
}
return request;
}
async function waitForThreadStart(harness: ClientHarness): Promise<{ id?: number }> {
return waitForRequest(harness, "thread/start");
}
describe("startCodexAttemptThread", () => {
beforeEach(() => {
vi.stubEnv("CODEX_API_KEY", "");
vi.stubEnv("OPENAI_API_KEY", "");
clearSharedCodexAppServerClient();
});
afterEach(() => {
clearSharedCodexAppServerClient();
vi.restoreAllMocks();
vi.unstubAllEnvs();
});
it("clears the shared app-server when top-level thread startup fails with an app error", async () => {
const { harness, run } = startThreadWithHarness(5_000);
await answerInitialize(harness);
const threadStart = await waitForThreadStart(harness);
harness.send({
id: threadStart.id,
error: { code: -32000, message: "401 authentication_error: Invalid bearer token" },
});
await expect(run).rejects.toThrow("Invalid bearer token");
expect(harness.process.stdin.destroyed).toBe(true);
});
it("clears the shared app-server when startup abandons an in-flight thread request", async () => {
const { harness, run } = startThreadWithHarness(2_000);
const runError = run.then(
() => undefined,
(error: unknown) => error,
);
await answerInitialize(harness);
await waitForThreadStart(harness);
const error = await runError;
expect(error).toBeInstanceOf(Error);
expect((error as Error).message).toBe("codex app-server startup timed out");
expect(harness.process.stdin.destroyed).toBe(true);
});
it("clears the shared app-server when cancellation abandons an in-flight thread request", async () => {
const abortController = new AbortController();
const { harness, run } = startThreadWithHarness(5_000, abortController.signal);
const runError = run.then(
() => undefined,
(error: unknown) => error,
);
await answerInitialize(harness);
await waitForThreadStart(harness);
abortController.abort();
const error = await runError;
expect(error).toBeInstanceOf(Error);
expect((error as Error).message).toBe("codex app-server startup aborted");
expect(harness.process.stdin.destroyed).toBe(true);
});
it("clears the shared app-server when a startup RPC times out", async () => {
const perRpcTimeoutPluginConfig = {
...pluginConfig,
appServer: { command: "codex", requestTimeoutMs: 100 },
computerUse: { enabled: true, marketplaceDiscoveryTimeoutMs: 1 },
} satisfies CodexPluginConfig;
const { harness, run } = startThreadWithHarness(5_000, new AbortController().signal, {
pluginConfig: perRpcTimeoutPluginConfig,
});
const runError = run.then(
() => undefined,
(error: unknown) => error,
);
await answerInitialize(harness);
await waitForRequest(harness, "plugin/list");
const error = await runError;
expect(error).toBeInstanceOf(Error);
expect((error as Error).message).toBe("plugin/list timed out");
expect(harness.process.stdin.destroyed).toBe(true);
});
});

View File

@@ -49,6 +49,7 @@ import {
} from "./shared-client.js";
import {
startOrResumeThread,
isCodexThreadStartRequestError,
type CodexAppServerThreadLifecycleBinding,
type CodexContextEngineThreadBootstrapProjection,
} from "./thread-lifecycle.js";
@@ -82,7 +83,6 @@ export async function startCodexAttemptThread(params: {
buildAttemptParams: () => EmbeddedRunAttemptParams;
sessionAgentId: string;
effectiveWorkspace: string;
effectiveCwd: string;
dynamicTools: CodexDynamicToolSpec[];
developerInstructions: string | undefined;
finalConfigPatch?: Parameters<typeof startOrResumeThread>[0]["finalConfigPatch"];
@@ -100,7 +100,7 @@ export async function startCodexAttemptThread(params: {
}): Promise<StartCodexAttemptThreadResult> {
let pluginAppServer = params.appServer;
let releaseSharedClientLease: (() => void) | undefined;
let startupClientForAbandonedRequestCleanup: CodexAppServerClient | undefined;
let startupClientForCleanup: CodexAppServerClient | undefined;
let releaseStartupResourcesOnTimeout: (() => Promise<void>) | undefined;
try {
const startupResult = await withCodexStartupTimeout({
@@ -185,7 +185,7 @@ export async function startCodexAttemptThread(params: {
};
releaseSharedClientLease = startupClientLease;
attemptedClient = startupClient;
startupClientForAbandonedRequestCleanup = startupClient;
startupClientForCleanup = startupClient;
await ensureCodexComputerUse({
client: startupClient,
pluginConfig: params.pluginConfig,
@@ -239,7 +239,7 @@ export async function startCodexAttemptThread(params: {
params.nativeToolSurfaceEnabled,
);
const startupExecutionCwd = resolveCodexAppServerExecutionCwd({
effectiveCwd: params.effectiveCwd,
effectiveWorkspace: params.effectiveWorkspace,
environment: startupSandboxEnvironment,
nativeToolSurfaceEnabled: params.nativeToolSurfaceEnabled,
});
@@ -334,8 +334,8 @@ export async function startCodexAttemptThread(params: {
}
const failedClient = attemptedClient;
const clearedSharedClient = clearSharedCodexAppServerClientIfCurrent(failedClient);
if (startupClientForAbandonedRequestCleanup === failedClient) {
startupClientForAbandonedRequestCleanup = undefined;
if (startupClientForCleanup === failedClient) {
startupClientForCleanup = undefined;
}
attemptedClient = undefined;
if (attempt >= CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS) {
@@ -365,7 +365,7 @@ export async function startCodexAttemptThread(params: {
throw new Error("codex app-server startup retry loop exited unexpectedly");
},
});
startupClientForAbandonedRequestCleanup = undefined;
startupClientForCleanup = undefined;
if (!releaseSharedClientLease) {
throw new Error("codex app-server startup succeeded without a shared client lease");
}
@@ -375,38 +375,12 @@ export async function startCodexAttemptThread(params: {
releaseSharedClientLease,
};
} catch (error) {
if (
params.signal.aborted ||
shouldClearSharedClientAfterStartupRace(error) ||
shouldClearSharedClientAfterStartupFailure({
error,
spawnedBy: params.spawnedBy,
})
) {
clearSharedCodexAppServerClientIfCurrent(startupClientForAbandonedRequestCleanup);
const transportPoisoned = isCodexAppServerConnectionClosedError(error);
const preserveSharedClientForSpawnedThreadStartError =
Boolean(params.spawnedBy?.trim()) && isCodexThreadStartRequestError(error);
if (transportPoisoned || !preserveSharedClientForSpawnedThreadStartError) {
clearSharedCodexAppServerClientIfCurrent(startupClientForCleanup);
}
throw error;
}
}
function shouldClearSharedClientAfterStartupRace(error: unknown): boolean {
return (
error instanceof Error &&
(error.message === "codex app-server startup timed out" ||
error.message === "codex app-server startup aborted" ||
error.message.endsWith(" timed out"))
);
}
function shouldClearSharedClientAfterStartupFailure(params: {
error: unknown;
spawnedBy: EmbeddedRunAttemptParams["spawnedBy"];
}): boolean {
if (!(params.error instanceof Error)) {
return !params.spawnedBy;
}
if (params.error.message.includes("write EPIPE")) {
return true;
}
return !params.spawnedBy;
}

View File

@@ -48,7 +48,6 @@ export type DynamicToolBuildParams = {
params: EmbeddedRunAttemptParams;
resolvedWorkspace: string;
effectiveWorkspace: string;
effectiveCwd?: string;
sandboxSessionKey: string;
sandbox: OpenClawSandboxContext;
nativeToolSurfaceEnabled?: boolean;
@@ -208,15 +207,11 @@ export async function buildDynamicTools(input: DynamicToolBuildParams) {
sessionId: params.sessionId,
runId: params.runId,
agentDir,
cwd: input.effectiveCwd ?? input.effectiveWorkspace,
workspaceDir: input.effectiveWorkspace,
spawnWorkspaceDir:
input.effectiveCwd && input.effectiveCwd !== input.effectiveWorkspace
? input.resolvedWorkspace
: resolveAttemptSpawnWorkspaceDir({
sandbox: input.sandbox,
resolvedWorkspace: input.resolvedWorkspace,
}),
spawnWorkspaceDir: resolveAttemptSpawnWorkspaceDir({
sandbox: input.sandbox,
resolvedWorkspace: input.resolvedWorkspace,
}),
config: params.config,
authProfileStore: params.toolAuthProfileStore ?? params.authProfileStore,
abortSignal: input.runAbortController.signal,
@@ -466,13 +461,13 @@ export function resolveCodexSandboxEnvironmentSelection(
}
export function resolveCodexAppServerExecutionCwd(params: {
effectiveCwd: string;
effectiveWorkspace: string;
environment?: CodexSandboxExecEnvironment;
nativeToolSurfaceEnabled: boolean;
}): string {
return params.environment && params.nativeToolSurfaceEnabled
? params.environment.cwd
: params.effectiveCwd;
: params.effectiveWorkspace;
}
export function resolveCodexExternalSandboxPolicyForOpenClawSandbox(

View File

@@ -1,9 +1,4 @@
import type { AgentToolResult } from "openclaw/plugin-sdk/agent-core";
import {
onInternalDiagnosticEvent,
waitForDiagnosticEventsDrained,
type DiagnosticEventPayload,
} from "openclaw/plugin-sdk/diagnostic-runtime";
import type { AnyAgentTool } from "openclaw/plugin-sdk/agent-harness";
import {
HEARTBEAT_RESPONSE_TOOL_NAME,
@@ -298,33 +293,18 @@ describe("createCodexDynamicToolBridge", () => {
it("quarantines dynamic tools with unsupported input schemas", async () => {
const warn = vi.spyOn(embeddedAgentLog, "warn").mockImplementation(() => undefined);
const diagnosticEvents: DiagnosticEventPayload[] = [];
const unsubscribeDiagnostics = onInternalDiagnosticEvent((event) =>
diagnosticEvents.push(event),
);
const badExecute = vi.fn();
let bridge!: ReturnType<typeof createCodexDynamicToolBridge>;
try {
bridge = createCodexDynamicToolBridge({
tools: [
createTool({ name: "message" }),
createTool({
name: "dofbot_move_angles",
parameters: { type: "array", items: { type: "number" } },
execute: badExecute,
}),
],
signal: new AbortController().signal,
hookContext: {
runId: "run-1",
sessionId: "session-1",
sessionKey: "agent:main:session-1",
},
});
await waitForDiagnosticEventsDrained();
} finally {
unsubscribeDiagnostics();
}
const bridge = createCodexDynamicToolBridge({
tools: [
createTool({ name: "message" }),
createTool({
name: "dofbot_move_angles",
parameters: { type: "array", items: { type: "number" } },
execute: badExecute,
}),
],
signal: new AbortController().signal,
});
expect(bridge.availableSpecs.map((tool) => tool.name)).toEqual(["message"]);
expect(bridge.specs.map((tool) => tool.name)).toEqual(["message"]);
@@ -345,23 +325,6 @@ describe("createCodexDynamicToolBridge", () => {
],
}),
);
const blockedEvents = diagnosticEvents.filter(
(
event,
): event is Extract<DiagnosticEventPayload, { type: "tool.execution.blocked" }> =>
event.type === "tool.execution.blocked",
);
expect(blockedEvents).toContainEqual(
expect.objectContaining({
type: "tool.execution.blocked",
runId: "run-1",
sessionId: "session-1",
sessionKey: "agent:main:session-1",
toolName: "dofbot_move_angles",
deniedReason: "unsupported_tool_schema",
reason: 'dofbot_move_angles.inputSchema.type must be "object"',
}),
);
const result = await bridge.handleToolCall({
threadId: "thread-1",

View File

@@ -1,5 +1,4 @@
import type { AgentToolResult } from "openclaw/plugin-sdk/agent-core";
import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
import {
createAgentToolResultMiddlewareRunner,
createCodexAppServerToolResultExtensionRunner,
@@ -119,7 +118,6 @@ export function createCodexDynamicToolBridge(params: {
...registeredProjection.quarantinedTools,
]);
warnQuarantinedDynamicTools(quarantinedTools);
emitQuarantinedDynamicToolDiagnostics(quarantinedTools, params.hookContext);
const telemetry: CodexDynamicToolBridge["telemetry"] = {
didSendViaMessagingTool: false,
messagingToolSentTexts: [],
@@ -339,23 +337,6 @@ function warnQuarantinedDynamicTools(tools: readonly CodexDynamicToolSchemaQuara
);
}
function emitQuarantinedDynamicToolDiagnostics(
tools: readonly CodexDynamicToolSchemaQuarantine[],
ctx: CodexDynamicToolHookContext | undefined,
): void {
for (const tool of tools) {
emitTrustedDiagnosticEvent({
type: "tool.execution.blocked",
runId: ctx?.runId,
sessionId: ctx?.sessionId,
sessionKey: ctx?.sessionKey,
toolName: tool.tool,
deniedReason: "unsupported_tool_schema",
reason: tool.violations.join(", "),
});
}
}
function dedupeQuarantinedDynamicTools(
tools: readonly CodexDynamicToolSchemaQuarantine[],
): CodexDynamicToolSchemaQuarantine[] {

View File

@@ -448,32 +448,9 @@ export type CodexSkillsListParams = {
forceReload?: boolean;
};
export type CodexSkillScope = "user" | "repo" | "system" | "admin";
export type CodexSkillMetadata = {
name: string;
description: string;
shortDescription?: string;
interface?: JsonObject;
dependencies?: JsonObject;
path: string;
scope: CodexSkillScope;
enabled: boolean;
};
export type CodexSkillErrorInfo = {
path: string;
message: string;
};
export type CodexSkillsListEntry = {
cwd: string;
skills: CodexSkillMetadata[];
errors: CodexSkillErrorInfo[];
};
export type CodexSkillsListResponse = {
data: CodexSkillsListEntry[];
data: JsonValue[];
nextCursor?: string | null;
};
export type CodexHooksListParams = {

View File

@@ -209,7 +209,6 @@ async function buildDynamicToolsForTest(
params,
resolvedWorkspace: workspaceDir,
effectiveWorkspace: workspaceDir,
effectiveCwd: params.cwd ?? workspaceDir,
sandboxSessionKey,
sandbox: { enabled: false, backendId: "docker" } as never,
nativeToolSurfaceEnabled: true,
@@ -900,46 +899,6 @@ describe("runCodexAppServerAttempt", () => {
expect(binding.mcpServersFingerprint).toBe("mcp-v2");
});
it("uses task cwd for Codex app-server requests while keeping bootstrap workspace separate", async () => {
const sessionFile = path.join(tempDir, "session.jsonl");
const workspaceDir = path.join(tempDir, "workspace");
const taskCwd = path.join(tempDir, "task-repo");
await fs.mkdir(workspaceDir, { recursive: true });
await fs.mkdir(taskCwd, { recursive: true });
await fs.writeFile(path.join(workspaceDir, "SOUL.md"), "workspace bootstrap", "utf8");
await fs.writeFile(path.join(taskCwd, "task-marker.txt"), "task marker", "utf8");
const appServer = createThreadLifecycleAppServerOptions();
const params = createParams(sessionFile, workspaceDir);
const requests: Array<{ method: string; params: unknown }> = [];
await startOrResumeThread({
client: {
getServerVersion: () => "0.132.0",
request: async (method: string, requestParams?: unknown) => {
requests.push({ method, params: requestParams });
if (method === "thread/start") {
return threadStartResult();
}
return {};
},
} as never,
params,
cwd: taskCwd,
dynamicTools: [],
appServer,
developerInstructions: "workspace bootstrap",
});
const threadStart = requests.find((request) => request.method === "thread/start");
expect((threadStart?.params as { cwd?: string } | undefined)?.cwd).toBe(taskCwd);
const turnStart = buildTurnStartParams(params, {
threadId: "thread-1",
cwd: taskCwd,
appServer,
});
expect(turnStart.cwd).toBe(taskCwd);
});
it("starts a no-MCP Codex thread when MCP config is evaluated empty", async () => {
const sessionFile = path.join(tempDir, "session.jsonl");
const workspaceDir = path.join(tempDir, "workspace");
@@ -1622,11 +1581,7 @@ describe("runCodexAppServerAttempt", () => {
expect(hookContext.sessionId).toBe("session-1");
const threadStart = harness.requests.find((request) => request.method === "thread/start");
const threadStartParams = threadStart?.params as { developerInstructions?: string } | undefined;
const wrappedPluginSystemContext = (text: string) =>
`---\n\nOpenClaw plugin-injected system context. This block is not workspace file content.\n\n${text}\n\n---`;
expect(threadStartParams?.developerInstructions).toContain(
`${wrappedPluginSystemContext("pre system")}\n\ncustom codex system\n\n${wrappedPluginSystemContext("post system")}`,
);
expect(threadStartParams?.developerInstructions).toContain("pre system\n\ncustom codex system");
const turnStart = harness.requests.find((request) => request.method === "turn/start");
const turnStartParams = turnStart?.params as
| { input?: Array<{ text?: string; text_elements?: unknown[]; type?: string }> }

View File

@@ -362,13 +362,6 @@ export async function runCodexAppServerAttempt(
? resolvedWorkspace
: sandbox.workspaceDir
: resolvedWorkspace;
const requestedCwd = params.cwd ? resolveUserPath(params.cwd) : undefined;
if (sandbox?.enabled && requestedCwd && requestedCwd !== resolvedWorkspace) {
throw new Error(
"cwd override is not supported for sandboxed Codex app-server runs; omit cwd or use the agent workspace as cwd",
);
}
const effectiveCwd = sandbox?.enabled ? effectiveWorkspace : (requestedCwd ?? effectiveWorkspace);
await ensureCodexWorkspaceDirOnce(effectiveWorkspace);
preDynamicStartupStages.mark("effective-workspace");
const appServer = resolveCodexAppServerForOpenClawToolPolicy({
@@ -525,7 +518,6 @@ export async function runCodexAppServerAttempt(
params,
resolvedWorkspace,
effectiveWorkspace,
effectiveCwd,
sandboxSessionKey,
sandbox,
nativeToolSurfaceEnabled,
@@ -542,7 +534,6 @@ export async function runCodexAppServerAttempt(
params,
resolvedWorkspace,
effectiveWorkspace,
effectiveCwd,
sandboxSessionKey,
sandbox,
nativeToolSurfaceEnabled,
@@ -606,7 +597,6 @@ export async function runCodexAppServerAttempt(
buildHarnessContextEngineRuntimeContext({
attempt: buildActiveRunAttemptParams(),
workspaceDir: effectiveWorkspace,
cwd: effectiveCwd,
agentDir,
activeAgentId: sessionAgentId,
contextEnginePluginId: activeContextEnginePluginId,
@@ -787,7 +777,7 @@ export async function runCodexAppServerAttempt(
});
const trajectoryRecorder = createCodexTrajectoryRecorder({
attempt: params,
cwd: effectiveCwd,
cwd: effectiveWorkspace,
developerInstructions: buildRenderedCodexDeveloperInstructions(),
prompt: codexTurnPromptText,
tools: toolBridge.availableSpecs,
@@ -805,7 +795,7 @@ export async function runCodexAppServerAttempt(
}
};
let codexEnvironmentSelection: CodexTurnEnvironmentParams[] | undefined;
let codexExecutionCwd = effectiveCwd;
let codexExecutionCwd = effectiveWorkspace;
let codexSandboxPolicy: CodexSandboxPolicy | undefined;
let restartContextEngineCodexThread:
| (() => Promise<CodexAppServerThreadLifecycleBinding>)
@@ -869,7 +859,6 @@ export async function runCodexAppServerAttempt(
buildAttemptParams: buildActiveRunAttemptParams,
sessionAgentId,
effectiveWorkspace,
effectiveCwd,
dynamicTools: toolBridge.specs,
developerInstructions: promptBuild.developerInstructions,
buildFinalConfigPatch: buildNativeHookRelayFinalConfigPatch,
@@ -913,7 +902,7 @@ export async function runCodexAppServerAttempt(
});
recordCodexTrajectoryContext(trajectoryRecorder, {
attempt: params,
cwd: effectiveCwd,
cwd: effectiveWorkspace,
developerInstructions: promptBuild.developerInstructions,
prompt: codexTurnPromptText,
tools: toolBridge.availableSpecs,
@@ -1857,7 +1846,6 @@ export async function runCodexAppServerAttempt(
agentId: sessionAgentId,
notifyUserMessagePersisted,
sessionKey: sandboxSessionKey,
cwd: effectiveCwd,
threadId: thread.threadId,
turnId: activeTurnId,
});
@@ -1982,7 +1970,6 @@ export async function runCodexAppServerAttempt(
notifyUserMessagePersisted,
result,
sessionKey: contextSessionKey,
cwd: effectiveCwd,
threadId: thread.threadId,
turnId: activeTurnId,
});
@@ -2023,7 +2010,6 @@ export async function runCodexAppServerAttempt(
runtimeContext: buildHarnessContextEngineRuntimeContextFromUsage({
attempt: buildActiveRunAttemptParams(),
workspaceDir: effectiveWorkspace,
cwd: effectiveCwd,
agentDir,
activeAgentId: sessionAgentId,
contextEnginePluginId: activeContextEnginePluginId,

View File

@@ -103,7 +103,6 @@ export async function mirrorTranscriptBestEffort(params: {
notifyUserMessagePersisted: (message: Extract<AgentMessage, { role: "user" }>) => void;
result: EmbeddedRunAttemptResult;
sessionKey?: string;
cwd: string;
threadId: string;
turnId: string;
}): Promise<void> {
@@ -117,8 +116,6 @@ export async function mirrorTranscriptBestEffort(params: {
sessionFile: params.params.sessionFile,
agentId: params.agentId,
sessionKey: params.sessionKey,
sessionId: params.params.sessionId,
cwd: params.cwd,
messages,
// Scope is thread-stable. Each entry in `messagesSnapshot` is tagged
// with a per-turn `attachCodexMirrorIdentity` value carrying its own
@@ -185,7 +182,6 @@ export async function mirrorPromptAtTurnStartBestEffort(params: {
agentId?: string;
notifyUserMessagePersisted: (message: Extract<AgentMessage, { role: "user" }>) => void;
sessionKey?: string;
cwd: string;
threadId: string;
turnId: string;
}): Promise<void> {
@@ -202,8 +198,6 @@ export async function mirrorPromptAtTurnStartBestEffort(params: {
sessionFile: params.params.sessionFile,
agentId: params.agentId,
sessionKey: params.sessionKey,
sessionId: params.params.sessionId,
cwd: params.cwd,
messages: [userPromptMessage],
idempotencyScope: `codex-app-server:${params.threadId}`,
config: params.params.config,
@@ -272,8 +266,6 @@ function buildMirrorDedupeIdentity(message: MirroredAgentMessage): string {
export async function mirrorCodexAppServerTranscript(params: {
sessionFile: string;
sessionId?: string;
cwd?: string;
sessionKey?: string;
agentId?: string;
messages: AgentMessage[];
@@ -334,8 +326,6 @@ export async function mirrorCodexAppServerTranscript(params: {
transcriptPath: params.sessionFile,
message: messageToAppend,
idempotencyLookup: idempotencyKey ? "caller-checked" : "scan",
sessionId: params.sessionId,
cwd: params.cwd,
config: params.config,
});
if (appendedMessage.role === "user") {

View File

@@ -57,7 +57,7 @@ export function formatCodexStatus(probes: CodexStatusProbes): string {
lines.push(
`Skills: ${
probes.skills.ok
? summarizeCodexSkills(probes.skills.value)
? summarizeArrayLike(probes.skills.value)
: formatCodexDisplayText(probes.skills.error)
}`,
);
@@ -199,48 +199,6 @@ export function formatList(response: JsonValue | undefined, label: string): stri
].join("\n");
}
export function formatSkills(response: JsonValue | undefined): string {
const groups = isJsonObject(response) && Array.isArray(response.data) ? response.data : [];
if (groups.length === 0) {
return "Codex skills: none returned.";
}
const lines = ["Codex skills:"];
let renderedSkills = 0;
let loadErrors = 0;
for (const group of groups) {
const record = isJsonObject(group) ? group : {};
if (Array.isArray(record.errors)) {
loadErrors += record.errors.length;
}
const skills = Array.isArray(record.skills) ? record.skills : [];
if (skills.length === 0) {
continue;
}
for (const skill of skills) {
if (isJsonObject(skill) && skill.enabled === false) {
continue;
}
lines.push(`- ${formatCodexSkillEntry(skill)}`);
renderedSkills += 1;
}
}
if (renderedSkills === 0) {
if (loadErrors > 0) {
return `Codex skills: none returned (${loadErrors} load ${
loadErrors === 1 ? "error" : "errors"
}).`;
}
return "Codex skills: none returned.";
}
return lines.join("\n");
}
function formatCodexSkillEntry(entry: JsonValue): string {
const record = isJsonObject(entry) ? entry : {};
const name = readString(record, "name") ?? "<unknown>";
return `\`${formatCodexDisplayText(name)}\``;
}
const CODEX_RESUME_SAFE_THREAD_ID_PATTERN = /^[A-Za-z0-9._:-]+$/;
function formatCodexResumeHint(threadId: string): string {
@@ -392,36 +350,6 @@ function summarizeArrayLike(value: JsonValue | undefined): string {
return `${entries.length}`;
}
function summarizeCodexSkills(value: JsonValue | undefined): string {
const groups = isJsonObject(value) && Array.isArray(value.data) ? value.data : [];
if (groups.length === 0) {
return "none returned";
}
let enabledSkills = 0;
let loadErrors = 0;
for (const group of groups) {
if (!isJsonObject(group)) {
continue;
}
if (Array.isArray(group.errors)) {
loadErrors += group.errors.length;
}
if (!Array.isArray(group.skills)) {
continue;
}
enabledSkills += group.skills.filter(
(skill) => !isJsonObject(skill) || skill.enabled !== false,
).length;
}
if (enabledSkills > 0) {
return `${enabledSkills}`;
}
if (loadErrors > 0) {
return `none returned (${loadErrors} load ${loadErrors === 1 ? "error" : "errors"})`;
}
return "none returned";
}
function formatCodexRateLimitSummary(value: JsonValue | undefined): string {
const summary = summarizeCodexRateLimits(value);
if (summary) {

View File

@@ -30,7 +30,6 @@ import {
formatCodexStatus,
formatList,
formatModels,
formatSkills,
formatThreads,
readString,
} from "./command-formatters.js";
@@ -381,8 +380,9 @@ export async function handleCodexSubcommand(
return { text: "Usage: /codex skills" };
}
return {
text: formatSkills(
text: formatList(
await deps.codexControlRequest(options.pluginConfig, CODEX_CONTROL_METHODS.listSkills, {}),
"Codex skills",
),
};
}
@@ -1932,8 +1932,7 @@ function parseCodexCliSessionsArgs(args: string[]): ParsedCodexCliSessionsArgs {
}
if (arg === "--limit") {
const value = readRequiredOptionValue(args, index);
const parsedLimit =
value && /^\+?\d+$/.test(value.trim()) ? Number(value.trim()) : Number.NaN;
const parsedLimit = value && /^\+?\d+$/.test(value.trim()) ? Number(value.trim()) : NaN;
if (!Number.isSafeInteger(parsedLimit) || parsedLimit <= 0) {
parsed.help = true;
continue;

View File

@@ -12,7 +12,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { CODEX_CONTROL_METHODS } from "./app-server/capabilities.js";
import type { CodexComputerUseStatus } from "./app-server/computer-use.js";
import type { CodexAppServerStartOptions } from "./app-server/config.js";
import type { JsonValue } from "./app-server/protocol.js";
import {
readRecentCodexRateLimits,
resetCodexRateLimitCacheForTests,
@@ -913,62 +912,6 @@ describe("codex command", () => {
expect(result.text).not.toContain("@here");
});
it("summarizes Codex status skill groups by enabled nested skills", async () => {
const deps = createDeps({
readCodexStatusProbes: vi.fn(async () => ({
models: { ok: true as const, value: { models: [] } },
account: { ok: true as const, value: {} },
limits: { ok: true as const, value: { rateLimits: null, rateLimitsByLimitId: null } },
mcps: { ok: true as const, value: { data: [] } },
skills: {
ok: true as const,
value: {
data: [
{
cwd: "/repo-a",
skills: [
{
name: "enabled-one",
description: "",
path: "/repo-a/.codex/skills/enabled-one/SKILL.md",
scope: "repo" as const,
enabled: true,
},
{
name: "disabled-one",
description: "",
path: "/repo-a/.codex/skills/disabled-one/SKILL.md",
scope: "repo" as const,
enabled: false,
},
],
errors: [],
},
{
cwd: "/repo-b",
skills: [
{
name: "enabled-two",
description: "",
path: "/repo-b/.codex/skills/enabled-two/SKILL.md",
scope: "repo" as const,
enabled: true,
},
],
errors: [{ path: "/repo-b/bad/SKILL.md", message: "bad skill" }],
},
],
},
},
})),
});
const result = await handleCodexCommand(createContext("status"), { deps });
expect(result.text).toContain("Skills: 2");
expect(result.text).not.toContain("Skills: 1");
});
it("summarizes generated Codex rate-limit payloads", async () => {
const limits = {
ok: true as const,
@@ -3142,120 +3085,19 @@ describe("codex command", () => {
const codexControlRequest = vi
.fn()
.mockResolvedValueOnce({ data: [{ name: "<@U123> [mcp](https://evil)" }] })
.mockResolvedValueOnce({
data: [
{
cwd: "/repo",
skills: [
{
name: "skill_1 @here",
description: "",
path: "/repo/.codex/skills/skill_1/SKILL.md",
scope: "repo",
enabled: true,
},
],
errors: [],
},
],
});
.mockResolvedValueOnce({ data: [{ id: "skill_1 @here" }] });
const deps = createDeps({ codexControlRequest });
const mcp = await handleCodexCommand(createContext("mcp"), { deps });
const skills = await handleCodexCommand(createContext("skills"), { deps });
expect(mcp.text).toContain("&lt;\uff20U123&gt; \uff3bmcp\uff3d\uff08https://evil\uff09");
expect(skills.text).toContain("- `skill\uff3f1 \uff20here`");
expect(skills.text).toContain("skill\uff3f1 \uff20here");
expect(`${mcp.text}\n${skills.text}`).not.toContain("<@U123>");
expect(`${mcp.text}\n${skills.text}`).not.toContain("[mcp](https://evil)");
expect(`${mcp.text}\n${skills.text}`).not.toContain("@here");
});
it("formats every Codex skill as a code-styled bullet and tolerates malformed entries", async () => {
const malformedSkillEntries: JsonValue[] = [
null,
{ description: "missing name" },
{
name: "final-skill",
description: "Final skill",
path: "/repo-b/.codex/skills/final-skill/SKILL.md",
scope: "repo",
enabled: true,
},
];
const codexControlRequest = vi.fn(async () => ({
data: [
{
cwd: "/repo-a",
skills: Array.from({ length: 26 }, (_, index) => ({
name: `skill-${index + 1}`,
description: `Skill ${index + 1}`,
path: `/repo-a/.codex/skills/skill-${index + 1}/SKILL.md`,
scope: "repo",
enabled: true,
})).concat({
name: "disabled-skill",
description: "Disabled skill",
path: "/repo-a/.codex/skills/disabled-skill/SKILL.md",
scope: "repo",
enabled: false,
}),
errors: [{ path: "/repo-a/bad/SKILL.md", message: "bad skill" }],
},
{
cwd: "/repo-b",
skills: malformedSkillEntries,
errors: [],
},
"malformed group",
],
}));
const deps = createDeps({ codexControlRequest });
const result = await handleCodexCommand(createContext("skills"), { deps });
expect(result.text).toContain("- `skill-1`");
expect(result.text).toContain("- `skill-26`");
expect(result.text).toContain("- `&lt;unknown&gt;`");
expect(result.text).toContain("- `final-skill`");
expect(result.text).not.toContain("Workspace:");
expect(result.text).not.toContain("Error:");
expect(result.text).not.toContain("More skills available");
expect(result.text).not.toContain("Skill 1");
expect(result.text).not.toContain("/repo-a/.codex/skills");
expect(result.text).not.toContain("disabled-skill");
});
it("reports Codex skill load errors when no skills render", async () => {
const codexControlRequest = vi.fn(async () => ({
data: [
{
cwd: "/repo-a",
skills: [
{
name: "disabled-skill",
description: "Disabled skill",
path: "/repo-a/.codex/skills/disabled-skill/SKILL.md",
scope: "repo",
enabled: false,
},
],
errors: [
{ path: "/repo-a/bad/SKILL.md", message: "bad skill <@U123>" },
{ path: "/repo-a/other/SKILL.md", message: "other bad skill @here" },
],
},
],
}));
const deps = createDeps({ codexControlRequest });
const result = await handleCodexCommand(createContext("skills"), { deps });
expect(result.text).toBe("Codex skills: none returned (2 load errors).");
expect(result.text).not.toContain("<@U123>");
expect(result.text).not.toContain("@here");
});
it("returns sanitized command failures instead of leaking app-server errors", async () => {
const sessionFile = path.join(tempDir, "session.jsonl");
await fs.writeFile(

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/comfy-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw ComfyUI provider plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/copilot-proxy",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw Copilot Proxy provider plugin",
"type": "module",

View File

@@ -1,6 +1,6 @@
{
"name": "@openclaw/deepgram-provider",
"version": "2026.5.28",
"version": "2026.5.27",
"private": true,
"description": "OpenClaw Deepgram media-understanding provider",
"type": "module",

Some files were not shown because too many files have changed in this diff Show More