fix(release): preserve shipped channel surfaces in npm tar#52913
fix(release): preserve shipped channel surfaces in npm tar#52913
Conversation
🔒 Aisle Security AnalysisWe found 3 potential security issue(s) in this PR:
1. 🟡 Untrusted official channel-catalog.json search paths allow catalog spoofing and arbitrary npmSpec suggestions
Description
Impact:
Vulnerable code (new behavior): const execDir = path.dirname(process.execPath);
candidates.push(path.join(execDir, OFFICIAL_CHANNEL_CATALOG_RELATIVE_PATH));
candidates.push(path.join(execDir, "channel-catalog.json"));
...
for (const entry of loadOfficialCatalogEntries(options)) {
const priority = ORIGIN_PRIORITY.bundled ?? 99;
... resolved.set(entry.id, { entry, priority });
}Why this is risky:
RecommendationRestrict “official” catalog loading to trusted, immutable locations and/or verify authenticity. Recommended fixes (pick one or combine):
Example: only load from package root resolved from function resolveOfficialCatalogPaths(): string[] {
const root = resolveOpenClawPackageRootSync({ moduleUrl: import.meta.url });
return root ? [path.join(root, OFFICIAL_CHANNEL_CATALOG_RELATIVE_PATH)] : [];
}
// When merging, do NOT treat as bundled
const priority = 98; // lower trust than bundled2. 🔵 Potential DoS via unbounded synchronous loading/parsing of plugin catalog JSON from configurable paths
Description
Impact:
Vulnerable code: const payload = JSON.parse(fs.readFileSync(resolvedPath, "utf-8")) as unknown;RecommendationAdd hard limits and safer loading when ingesting catalog files:
Example (size guard): const MAX_BYTES = 5 * 1024 * 1024;
const stat = fs.statSync(resolvedPath);
if (!stat.isFile() || stat.size > MAX_BYTES) {
continue; // or log/throw
}
const text = fs.readFileSync(resolvedPath, "utf-8");
const payload = JSON.parse(text) as unknown;3. 🔵 Prototype pollution via untrusted channel IDs used as object keys in UI catalog builders
Description
Vulnerable code: labels[entry.id] = entry.label;
detailLabels[entry.id] = entry.detailLabel;
...
byId[entry.id] = entry;RecommendationPrevent prototype pollution by ensuring these dictionaries cannot have their prototype mutated and/or by rejecting dangerous keys. Option A (preferred): create null-prototype maps const labels: Record<string, string> = Object.create(null);
const detailLabels: Record<string, string> = Object.create(null);
const systemImages: Record<string, string> = Object.create(null);
const byId: Record<string, ChannelUiMetaEntry> = Object.create(null);Option B: validate IDs before using them as keys function isSafeKey(key: string): boolean {
return key !== "__proto__" && key !== "prototype" && key !== "constructor";
}
...
if (!isSafeKey(entry.id)) continue;
byId[entry.id] = entry;Doing both is even safer. Also consider validating Analyzed PR: #52913 at commit Last updated on: 2026-03-23T15:53:09Z |
Greptile SummaryThis PR fixes the Key changes:
Confidence Score: 5/5
|
| for (const entry of loadOfficialCatalogEntries(options)) { | ||
| const priority = ORIGIN_PRIORITY.bundled ?? 99; | ||
| const existing = resolved.get(entry.id); | ||
| if (!existing || priority < existing.priority) { | ||
| resolved.set(entry.id, { entry, priority }); | ||
| } | ||
| } |
There was a problem hiding this comment.
Official catalog silently overrides user external-catalog customizations
Official catalog entries are inserted with ORIGIN_PRIORITY.bundled (= 3), and external catalog entries are only appended via if (!resolved.has(entry.id)) (line 415). This means that once a channel ID appears in the official catalog, any user-defined external catalog entry for the same channel ID (set via OPENCLAW_PLUGIN_CATALOG_PATHS or the default config-dir catalog) is silently ignored.
Before this PR, in a packaged install where bundled metadata was absent, external catalog entries for official channels (e.g. a user pointing at a self-hosted @openclaw/whatsapp fork) would still be respected. After this PR they are not, because the official catalog entry takes the slot first.
If this is intentional (official channels should not be user-overridable via external catalogs), it's worth a short in-code comment so the next reader doesn't mistake the skip for a bug. If users should be able to override, the official catalog priority should be set higher (e.g. ORIGIN_PRIORITY.bundled + 1 or a dedicated official origin) so that external entries can replace it:
// current – official cannot be overridden by external catalog
const priority = ORIGIN_PRIORITY.bundled ?? 99; // 3
// alternative – give official a lower-priority slot
const priority = (ORIGIN_PRIORITY.bundled ?? 3) + 1; // 4, so external-catalog winsPrompt To Fix With AI
This is a comment left during a code review.
Path: src/channels/plugins/catalog.ts
Line: 403-409
Comment:
**Official catalog silently overrides user external-catalog customizations**
Official catalog entries are inserted with `ORIGIN_PRIORITY.bundled` (= 3), and external catalog entries are only appended via `if (!resolved.has(entry.id))` (line 415). This means that once a channel ID appears in the official catalog, any user-defined external catalog entry for the same channel ID (set via `OPENCLAW_PLUGIN_CATALOG_PATHS` or the default config-dir catalog) is silently ignored.
Before this PR, in a packaged install where bundled metadata was absent, external catalog entries for official channels (e.g. a user pointing at a self-hosted `@openclaw/whatsapp` fork) would still be respected. After this PR they are not, because the official catalog entry takes the slot first.
If this is intentional (official channels should not be user-overridable via external catalogs), it's worth a short in-code comment so the next reader doesn't mistake the skip for a bug. If users should be able to override, the official catalog priority should be set higher (e.g. `ORIGIN_PRIORITY.bundled + 1` or a dedicated `official` origin) so that external entries can replace it:
```typescript
// current – official cannot be overridden by external catalog
const priority = ORIGIN_PRIORITY.bundled ?? 99; // 3
// alternative – give official a lower-priority slot
const priority = (ORIGIN_PRIORITY.bundled ?? 3) + 1; // 4, so external-catalog wins
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8bee5a73a3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for (const entry of loadOfficialCatalogEntries(options)) { | ||
| const priority = ORIGIN_PRIORITY.bundled ?? 99; | ||
| const existing = resolved.get(entry.id); | ||
| if (!existing || priority < existing.priority) { | ||
| resolved.set(entry.id, { entry, priority }); | ||
| } |
There was a problem hiding this comment.
Preserve external catalog precedence for official channels
When OPENCLAW_PLUGIN_CATALOG_PATHS / OPENCLAW_MPM_CATALOG_PATHS contains an entry for an official channel ID (for example to point whatsapp at a private mirror or fork), this new loop inserts the shipped catalog entry first with bundled priority. The later external-catalog pass only adds entries that are not already present (src/channels/plugins/catalog.ts:411-417), so the custom install.npmSpec is ignored and channel setup will install the hard-coded @openclaw/* package instead (src/commands/channel-setup/plugin-install.ts:185-203). Before this commit, those external entries were the only source for missing official channels, so this is a regression for catalog-driven installs.
Useful? React with 👍 / 👎.
|
@greptile-apps please re-review this PR. New work since the last Greptile review:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2f1f4100ec
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| export function shouldIncludeOptionalBundledClusters(env = process.env) { | ||
| return env[OPTIONAL_BUNDLED_BUILD_ENV] === "1"; | ||
| // Release artifacts should preserve the last shipped upgrade surface by | ||
| // default. Specific size-sensitive lanes can still opt out explicitly. | ||
| return env[OPTIONAL_BUNDLED_BUILD_ENV] !== "0"; |
There was a problem hiding this comment.
Keep optional bundled clusters opt-in for default builds
With OPENCLAW_INCLUDE_OPTIONAL_BUNDLED unset (the normal pnpm build / CI path), this now returns true, and both tsdown.config.ts:87-93 and scripts/copy-bundled-plugin-metadata.mjs:192-194 start treating every optional cluster as bundled. That means the release build now compiles and ships the full dist/extensions/whatsapp, matrix, etc. trees again, instead of only adding the new catalog metadata. Besides the pack-size jump already reflected in this PR, that broadens the bundled runtime surface for every stable/beta install and undermines the “discoverability only” fix described here.
Useful? React with 👍 / 👎.
| const requiredOptionalBundledPackPaths = [ | ||
| "dist/control-ui/index.html", | ||
| ...optionalBundledClusters | ||
| .filter((cluster) => cluster !== "ui") | ||
| .flatMap((cluster) => [ | ||
| `dist/extensions/${cluster}/openclaw.plugin.json`, | ||
| `dist/extensions/${cluster}/package.json`, | ||
| ]), | ||
| ]; |
There was a problem hiding this comment.
Respect explicit optional-bundle opt-out in release-check
This new required-path list is unconditional, so pnpm release:check now fails whenever a build intentionally sets OPENCLAW_INCLUDE_OPTIONAL_BUNDLED=0. That opt-out is still advertised in shouldIncludeOptionalBundledClusters(), and scripts/copy-bundled-plugin-metadata.mjs:192-194 will remove those dist/extensions/* directories in that mode, but collectMissingPackPaths() now always requires them. As written, any size-sensitive lane that disables optional bundles can no longer pass release verification.
Useful? React with 👍 / 👎.
2f1f410 to
7bd8d7d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7bd8d7d7d7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "protocol:gen": "node --import tsx scripts/protocol-gen.ts", | ||
| "protocol:gen:swift": "node --import tsx scripts/protocol-gen-swift.ts", | ||
| "release:check": "pnpm config:docs:check && pnpm plugin-sdk:api:check && node scripts/stage-bundled-plugin-runtime-deps.mjs && node --import tsx scripts/release-check.ts", | ||
| "release:check": "pnpm config:docs:check && pnpm plugin-sdk:api:check && node scripts/stage-bundled-plugin-runtime-deps.mjs && pnpm ui:build && node --import tsx scripts/release-check.ts", |
There was a problem hiding this comment.
Keep release-check from rebuilding Control UI assets
In .github/workflows/ci.yml, the release-check job downloads the dist/ artifact from build-artifacts and then runs pnpm release:check. Adding pnpm ui:build here means the validator regenerates dist/control-ui locally before npm pack --dry-run, so this guard no longer verifies the artifact that was actually built and uploaded. If build-artifacts ever omits or ships stale Control UI files again, release-check will repair them in place and still pass, which defeats the regression check this PR is trying to add.
Useful? React with 👍 / 👎.
| const packageRoots = [ | ||
| resolveOpenClawPackageRootSync({ cwd: process.cwd() }), | ||
| resolveOpenClawPackageRootSync({ moduleUrl: import.meta.url }), | ||
| ].filter((entry, index, all): entry is string => Boolean(entry) && all.indexOf(entry) === index); |
There was a problem hiding this comment.
Derive official catalog paths from the selected bundle tree
listChannelPluginCatalogEntries() already threads options.env through plugin discovery and external catalog loading, but this helper ignores that env and always anchors the official fallback to the current checkout / executable. In flows that point OPENCLAW_BUNDLED_PLUGINS_DIR at a different install tree, channel-setup callers like src/commands/channels/add.ts and src/commands/onboard-channels.ts will still merge official entries from the wrong build (or miss the catalog next to the chosen bundle), so discovery can advertise channels that do not belong to the selected runtime.
Useful? React with 👍 / 👎.
…52913) * fix(channels): ship official channel catalog (openclaw#52838) * fix(release): keep shipped bundles in npm tar (openclaw#52838) * build(release): fix rebased release-check helpers (openclaw#52838)
…52913) * fix(channels): ship official channel catalog (openclaw#52838) * fix(release): keep shipped bundles in npm tar (openclaw#52838) * build(release): fix rebased release-check helpers (openclaw#52838)
…52913) * fix(channels): ship official channel catalog (openclaw#52838) * fix(release): keep shipped bundles in npm tar (openclaw#52838) * build(release): fix rebased release-check helpers (openclaw#52838)
…52913) * fix(channels): ship official channel catalog (openclaw#52838) * fix(release): keep shipped bundles in npm tar (openclaw#52838) * build(release): fix rebased release-check helpers (openclaw#52838)
…52913) * fix(channels): ship official channel catalog (openclaw#52838) * fix(release): keep shipped bundles in npm tar (openclaw#52838) * build(release): fix rebased release-check helpers (openclaw#52838)
…52913) * fix(channels): ship official channel catalog (openclaw#52838) * fix(release): keep shipped bundles in npm tar (openclaw#52838) * build(release): fix rebased release-check helpers (openclaw#52838)
…claw#105) * fix(web-search): mark DuckDuckGo experimental * docs(tools): update DuckDuckGo Search for landed plugin code - Mark as experimental (not just unofficial) - Add region and safeSearch tool parameters (from DDG schema) - Add plugin config example for region/safeSearch defaults - Document auto-detection order (100 = last) - Note SafeSearch defaults to moderate - Verified against extensions/duckduckgo/src/ * fix(agents): deny local MEDIA paths for MCP results * Usage: include reset and deleted session archives (openclaw#43215) Merged via squash. Prepared head SHA: 49ed6c2 Co-authored-by: rcrick <23069968+rcrick@users.noreply.github.com> Co-authored-by: frankekn <4488090+frankekn@users.noreply.github.com> Reviewed-by: @frankekn * docs(tools): soften DDG wording (scrapes -> pulls/gathers) * fix(build): add stable memory-cli dist entry (openclaw#51759) Co-authored-by: oliviareid-svg <269669958+oliviareid-svg@users.noreply.github.com> Co-authored-by: Frank <vibespecs@gmail.com> * refactor!: drop legacy CLAWDBOT env compatibility * refactor!: remove moltbot state-dir migration fallback * fix(gateway): preserve async hook ingress provenance * fix(ci): write dist build stamp after builds * perf: trim vitest hot imports and refresh manifests * fix(security): unwrap time dispatch wrappers * fix(plugin-sdk): fall back to src root alias files * fix(ci): skip docs-only preflight pnpm audit * docs(changelog): note time exec approval fix * docs: refresh plugin-sdk api baseline * fix(runtime): make dist-runtime staging idempotent * fix(media): bound remote error-body snippet reads * fix(gateway): gate internal command persistence mutations * fix: restrict remote marketplace plugin sources * fix(runtime): skip peer resolution for bundled plugin deps * docs(agents): prefer current test model examples * fix(exec): escape invisible approval filler chars * test(models): refresh example model fixtures * fix(security): unify dispatch wrapper approval hardening * fix(security): harden explicit-proxy SSRF pinning * fix: gate synology chat reply name matching * docs: clarify sessions_spawn ACP vs subagent policies * refactor(exec): split wrapper resolution modules * refactor(exec): make dispatch wrapper semantics spec-driven * refactor(exec): share wrapper trust planning * refactor(exec): rename wrapper plans for trust semantics * fix: include .npmrc in onboard docker build * test: trim docker live auth mounts * Docs: refresh config baseline for Synology Chat * refactor: clarify synology delivery identity names * refactor: centralize synology dangerous name matching * refactor: narrow synology legacy name lookup * refactor: audit synology dangerous name matching * refactor: dedupe synology config schema * fix: normalize scoped vitest filter paths * fix(voice-call): harden webhook pre-auth guards * fix(synology-chat): fail closed shared webhook paths * docs: credit nexrin in synology changelog * test: fix base vitest thread regressions * test: finish base vitest thread fixture fixes * test(voice-call): accept oversize webhook socket resets * test: honor env auth in gateway live probes * fix: harden plugin docker e2e * Docs: align MiniMax examples with M2.7 * fix(ci): restore stale guardrails and baselines * Test: isolate qr dashboard integration suite * Gateway: resolve fallback plugin context lazily * fix: bind bootstrap setup codes to node profile * fix(tlon): unify settings reconciliation semantics * refactor(synology-chat): type startup webhook path policy * docs(synology-chat): clarify multi-account webhook paths * refactor: unify minimax model and failover live policies * docs: sync minimax m2.7 references * fix: harden Windows Parallels smoke installs * docs: reorder unreleased changelog by user impact * refactor: remove embedded runner cwd mutation * Infra: support shell carrier allow-always approvals * refactor: centralize bootstrap profile handling * refactor: reuse canonical setup bootstrap profile * fix(plugin-sdk): resolve hashed diagnostic events chunks * fix(plugin-sdk): normalize hashed diagnostic event exports * test: fix ci env-sensitive assertions * fix(gateway): fail closed on unresolved discovery endpoints * feat: add slash plugin installs * fix(media): block remote-host file URLs in loaders * fix(media): harden secondary local path seams * test: harden no-isolate reply teardown * docs(changelog): add Windows media security fix * refactor(gateway): centralize discovery target handling * test: narrow live transcript scaffolding strip * test: fix ci docs drift and bun qr exit handling * fix(browser): enforce node browser proxy allowProfiles * refactor(media): share local file access guards * test: stabilize ci test harnesses * test: harden no-isolate test module resets * fix(plugins): preserve live hook registry during gateway runs * test: fix channel summary registry setup * test: harden isolated test mocks * chore(plugins): remove opik investigation checkpoints * ACPX: align pinned runtime version (openclaw#52730) * ACPX: align pinned runtime version * ACPX: drop version example from help text * test: stop leaking image workspace temp dirs * fix(android): gate canvas bridge to trusted pages (openclaw#52722) * fix(android): gate canvas bridge to trusted pages * fix(changelog): note android canvas bridge gating * Update apps/android/app/src/main/java/ai/openclaw/app/node/CanvasActionTrust.kt Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * fix(android): snapshot canvas URL on UI thread --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * test: isolate base vitest thread blockers * fix: sync agent and autoreply e2e updates * test: harden no-isolate mocked module resets * docs: reorder unreleased changelog * fix(changelog): note windows media path guardrails (openclaw#52738) * fix: alphabetize web search provider listings * docs: clarify unreleased breaking changes * test: harden ci isolated mocks * fix: align websocket stream fallback types * test: finish no-isolate suite hardening * style: format image-generation runtime tests * fix(memory-core): register memory tools independently to prevent coupled failure (openclaw#52668) Merged via admin squash because current required CI failures are inherited from base and match latest `main` failures outside this PR's `memory-core` surface. Prepared head SHA: df7f968 Co-authored-by: artwalker <44759507+artwalker@users.noreply.github.com> Reviewed-by: @frankekn * fix(status): recompute fallback context window (openclaw#51795) * fix(status): recompute fallback context window * fix(status): keep live context token caps on fallback * fix(status): preserve fallback runtime context windows * fix(status): preserve configured fallback context caps * fix(status): keep provider-aware transcript context lookups * fix(status): preserve explicit fallback context caps * fix(status): clamp fallback configured context caps * fix(status): keep raw runtime slash ids * fix(status): refresh plugin-sdk api baseline * fix(status): preserve fallback context lookup * test(status): refresh plugin-sdk api baseline * fix(status): keep runtime slash-id context lookup --------- Co-authored-by: create <create@createdeMacBook-Pro.local> Co-authored-by: Frank Yang <frank.ekn@gmail.com> Co-authored-by: RichardCao <RichardCao@users.noreply.github.com> * fix(telegram): make buttons schema optional in message tool The Telegram plugin injects a `buttons` property into the message tool schema via `createMessageToolButtonsSchema()`, but without wrapping it in `Type.Optional()`. This causes TypeBox to include `buttons` in the JSON Schema `required` array. In isolated sessions (e.g. cron jobs) where no `currentChannel` is set, all plugin schemas are merged into the message tool. When the LLM calls the message tool without a `buttons` parameter, AJV validation fails with: `buttons: must have required property 'buttons'`. Wrap the buttons schema in `Type.Optional()` so it is not required. * fix: keep message-tool buttons optional for Telegram and Mattermost (openclaw#52589) (thanks @tylerliu612) * test: update codex test fixtures to gpt-5.4 * fix: repair runtime seams after rebase * fix: restore Telegram topic announce delivery (openclaw#51688) (thanks @mvanhorn) When `replyLike.text` or `replyLike.caption` is an unexpected non-string value (edge case from some Telegram API responses), the reply body was coerced to "[object Object]" via string concatenation. Add a `typeof === "string"` guard to gracefully fall back to empty string, matching the existing pattern used for `quoteText` in the same function. Co-authored-by: Penchan <penchan@penchan.co> * docs: sync generated release baselines * test: isolate pi embedded model thread fixtures * fix: restore provider runtime lazy boundary * fix: preserve Telegram reply context text (openclaw#50500) (thanks @p3nchan) * fix: guard Telegram reply context text (openclaw#50500) (thanks @p3nchan) * fix: preserve Telegram reply caption fallback (openclaw#50500) (thanks @p3nchan) --------- Co-authored-by: Ayaan Zaidi <hi@obviy.us> * fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush) * fix: increase shutdown timeout to avoid SIGTERM hang * fix(telegram): abort polling fetch on shutdown to prevent SIGTERM hang * fix(gateway): enforce hard exit on shutdown timeout for SIGTERM * fix: tighten gateway shutdown watchdog * fix: harden gateway SIGTERM shutdown (openclaw#51242) (thanks @juliabush) --------- Co-authored-by: Ayaan Zaidi <hi@obviy.us> * build: prepare 2026.3.22-beta.1 * fix: restore provider runtime lazy boundary * test: add parallels npm update smoke * test: split pi embedded model thread fixtures * fix: stop browser server tests from launching real chrome * test: stabilize live provider docker probes * fix: restart windows gateway after npm update * test: isolate server-context browser harness imports * test: inject model runtime hooks for thread-safe tests * test: snapshot ci timeout investigation * test: target gemini 3.1 flash alias * test: stabilize trigger handling and hook e2e tests * build: prepare 2026.3.22 * test: harden channel suite isolation * test: inject thread-safe deps for agent tools * test: raise timeout for slow provider auth normalization * ci: stabilize windows and bun unit lanes * test: inject thread-safe gateway and ACP seams * test: isolate pi model and reset-model thread fixtures * build: prepare 2026.3.23 * test: inject image-tool provider deps for raw threads * test: stabilize e2e module isolation * test: decouple vitest config checks from ambient env * fix: harden parallels smoke agent invocation * test: avoid repo-root perf profile artifacts * test: inject thread-safe base seams * fix: document Telegram asDocument alias (openclaw#52461) (thanks @bakhtiersizhaev) * feat(telegram): add asDocument param to message tool Adds `asDocument` as a user-facing alias for the existing `forceDocument` parameter in the message tool. When set to `true`, media files (images, videos, GIFs) are sent via `sendDocument` instead of `sendPhoto`/ `sendVideo`/`sendAnimation`, preserving the original file quality without Telegram compression. This is useful when agents need to deliver high-resolution images or uncompressed files to users via Telegram. `asDocument` is intentionally an alias rather than a replacement — the existing `forceDocument` continues to work unchanged. Changes: - src/agents/tools/message-tool.ts: add asDocument to send schema - src/agents/tools/telegram-actions.ts: OR asDocument into forceDocument - src/infra/outbound/message-action-runner.ts: same OR logic for outbound path - extensions/telegram/src/channel-actions.ts: read and forward asDocument - src/channels/plugins/actions/actions.test.ts: add test case * fix: restore channel-actions.ts to main version (rebase conflict fix) * fix(test): match asDocument test payload to actual params structure * fix(telegram): preserve forceDocument alias semantics * fix: document Telegram asDocument alias (openclaw#52461) (thanks @bakhtiersizhaev) --------- Co-authored-by: Бахтиер Сижаев <bkh@MacBook-Air.local> Co-authored-by: Ayaan Zaidi <hi@obviy.us> * fix: refactor deepseek bundled plugin (openclaw#48762) (thanks @07akioni) * fix: declare typebox runtime dep for mattermost plugin * test: reset line webhook mocks between cases * test: split attempt spawn-workspace thread fixtures * test: remove replaced spawn-workspace monolith * refactor: isolate attempt context engine thread helpers * CI: remove npm release preview workflow (openclaw#52825) * CI: remove npm release preview workflow * Docs: align release maintainer skill with manual publish * Docs: expand release maintainer skill flow * test: stabilize gateway thread harness * test: fix status plugin pagination expectation * test: harden channel suite isolation * build: sync lockfile for mattermost plugin * fix: ensure env proxy dispatcher before MiniMax and OpenAI Codex OAuth flows (openclaw#52228) Verified: - pnpm install --frozen-lockfile - NPM_CONFIG_CACHE=/tmp/openclaw-npm-cache-52228 pnpm build - pnpm check - pnpm test:macmini (failed on inherited pre-existing plugin contract test: src/plugins/contracts/registry.contract.test.ts missing deepseek in bundled provider contract registry outside this PR surface) Co-authored-by: openperf <80630709+openperf@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> * fix: restore ci gates * test: stabilize channel ci gate * docs: refresh generated config baseline * release: verify control-ui assets are included in npm tarball * release-check: include stderr/stdout when npm pack fails * release: add changelog for control UI tarball check * fix: keep session transcript pointers fresh after compaction (openclaw#50688) Co-authored-by: Frank Yang <frank.ekn@gmail.com> * fix(msteams): isolate probe test env credentials * release: automate macOS publishing (openclaw#52853) * release: automate macOS publishing * release: keep mac appcast in openclaw repo * release: add preflight-only release workflow runs * release: keep appcast updates manual * release: generate signed appcast as workflow artifact * release: require preflight before publish * release: require mac app for every release * docs: clarify every release ships mac app * release: document Sparkle feed and SHA rules * release: keep publish flow tag-based * release: stabilize mac appcast flow * release: document local mac fallback * Update CHANGELOG.md * Improve PR template regression prompts * fix(agents): preserve anthropic thinking block order (openclaw#52961) * fix(release): ship bundled plugins in pack artifacts * fix(config): keep built-in channels out of plugin allowlists (openclaw#52964) * fix(config): keep built-in channels out of plugin allowlists * docs(changelog): note doctor whatsapp allowlist fix * docs(changelog): move doctor whatsapp fix to top * Update CHANGELOG.md * fix(config): keep built-in auto-enable idempotent * fix(release): preserve shipped channel surfaces in npm tar (openclaw#52913) * fix(channels): ship official channel catalog (openclaw#52838) * fix(release): keep shipped bundles in npm tar (openclaw#52838) * build(release): fix rebased release-check helpers (openclaw#52838) * fix(gateway): harden supervised lock and browser attach readiness * fix(matrix): avoid duplicate runtime api exports * fix(gateway): avoid probe false negatives after connect * docs(changelog): note release and matrix fixes * fix(plugins): unblock Discord/Slack message tool sends and Feishu media (openclaw#52991) * fix(plugins): unblock Discord and Slack message tool payloads * docs(changelog): note Discord Slack and Feishu message fixes * fix(channels): preserve external catalog overrides (openclaw#52988) * fix(channels): preserve external catalog overrides * fix(channels): clarify catalog precedence * fix(channels): respect overridden install specs * fix(gateway): require admin for agent session reset * fix(voice-call): stabilize plivo v2 replay keys * fix(gateway): require auth for canvas routes * fix(clawhub): resolve auth token for skill browsing (openclaw#53017) * fix(clawhub): resolve auth token for skill browsing * docs(changelog): note clawhub skill auth fix * fix(release): raise npm pack size budget * Tests: fix fresh-main regressions (openclaw#53011) * Tests: fix fresh-main regressions * Tests: avoid chat notice cache priming --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org> * fix(config): ignore stale plugin allow entries * fix(browser): reuse running loopback browser after probe miss * fix(clawhub): honor macOS auth config path (openclaw#53034) * docs: fix nav ordering, missing pages, and stale model references - Sort providers alphabetically in docs.json nav - Sort channels alphabetically in docs.json nav (slack before synology-chat) - Add install/migrating-matrix to Maintenance nav section (was orphaned) - Remove zh-CN/plugins/architecture from nav (file does not exist) - Add Voice Call to channels index page - Add missing providers to providers index (DeepSeek, GitHub Copilot, OpenCode Go, Synthetic) - Sort providers index alphabetically - Update stale claude-3-5-sonnet model reference to claude-sonnet-4-6 in webhook docs * fix(clawhub): preserve XDG auth path on macOS * Agents: fix runtime web_search provider selection (openclaw#53020) Co-authored-by: Vincent Koc <vincentkoc@ieee.org> * docs: fix CLI command tree, SDK import path, and tool group listing - Remove non-existent 'secrets migrate' from CLI command tree - Add actual secrets subcommands: audit, configure, apply - Add missing plugin subcommands: inspect, uninstall, update, marketplace list - Fix plugins info -> inspect (actual command name) - Add message send and broadcast subcommands to command tree - Remove misleading deprecated import from sdk-overview - Add sessions_yield and subagents to group:sessions tool group docs - Fix formatting * fix(gateway): guard openrouter auto pricing recursion (openclaw#53055) * test: refresh thread-safe agent fixtures * Release: fix npm release preflight under pnpm (openclaw#52985) Co-authored-by: Vincent Koc <vincentkoc@ieee.org> * docs(changelog): add channel catalog override note (openclaw#52988) (openclaw#53059) * fix: harden update dev switch and refresh changelog * fix(mistral): repair max-token defaults and doctor migration (openclaw#53054) * fix(mistral): repair max-token defaults and doctor migration * fix(mistral): add missing small-model repair cap * fix(plugins): enable bundled Brave web search plugin by default (openclaw#52072) Brave is a bundled web search plugin but was missing from BUNDLED_ENABLED_BY_DEFAULT, causing it to be filtered out during provider resolution. This made web_search unavailable even when plugins.entries.brave.enabled was configured. Fixes openclaw#51937 Co-authored-by: Ubuntu <ubuntu@ip-172-26-10-234.us-west-2.compute.internal> Co-authored-by: Vincent Koc <vincentkoc@ieee.org> * fix(release): fail empty control ui tarballs * Revert "fix(plugins): enable bundled Brave web search plugin by default (openclaw#52072)" This reverts commit 0ea3c4d. * Telegram: preserve inbound debounce order * Telegram: fix fire-and-forget debounce order * fix(reply): refresh followup drain callbacks * Update CHANGELOG.md * fix(reply): preserve no-debounce inbound concurrency * fix(reply): clear idle followup callbacks * fix(inbound): bound tracked debounce keys * fix: preserve debounce and followup ordering (openclaw#52998) (thanks @osolmaz) * fix(discord): reply on native command auth failures (openclaw#53072) * docs(changelog): add missing recent fixes * fix: bound tracked debounce key accounting * fix packaged control ui asset lookup (openclaw#53081) * fix(cli): preserve posix default git dir * build: prepare 2026.3.23-beta.1 * test: harden canvas host undici isolation * docs(changelog): credit web search runtime fix * fix(openai-codex): bootstrap proxy on oauth refresh (openclaw#53078) Verified: - pnpm install --frozen-lockfile - pnpm exec vitest run extensions/openai/openai-codex-provider.runtime.test.ts extensions/openai/openai-provider.test.ts * release: harden preflight workflows (openclaw#53087) * release: harden preflight-only workflows * release: require main for publish runs * release: select xcode for macos workflow * release: retry flaky macos preflight steps * ci: shard bun test lane * Fix Control UI operator.read scope handling (openclaw#53110) Preserve Control UI scopes through the device-auth bypass path, normalize implied operator device-auth scopes, ignore cached under-scoped operator tokens, and degrade read-backed main pages gracefully when a connection truly lacks operator.read. Co-authored-by: Val Alexander <68980965+BunsDev@users.noreply.github.com> * build: prepare 2026.3.23 * fix(agents): prefer runtime snapshot for skill secrets * docs(changelog): note skill secretref runtime fix * fix(memory): bootstrap lancedb runtime on demand (openclaw#53111) Bootstrap LanceDB into plugin runtime state on first use for packaged/global installs, keep @lancedb/lancedb plugin-local, and add regression coverage for bundled, cached, retry, and Nix fail-fast runtime paths. Co-authored-by: Val Alexander <68980965+BunsDev@users.noreply.github.com> * build: finalize 2026.3.23 release * release: upload macos preflight artifacts (openclaw#53105) * release: upload macos preflight artifacts * release: speed up macos preflight * release: use xlarge macos runner * release: skip dmg path in macos preflight * fix(subagents): recheck timed-out announce waits (openclaw#53127) Recheck timed-out subagent announce waits against the latest runtime snapshot before announcing timeout, and keep that recheck best-effort so transient gateway failures do not suppress the announcement. Co-authored-by: Val Alexander <68980965+BunsDev@users.noreply.github.com> * docs(feishu): replace botName with name in config examples (openclaw#52753) Merged via squash. Prepared head SHA: 5237726 Co-authored-by: haroldfabla2-hue <229189334+haroldfabla2-hue@users.noreply.github.com> Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com> Reviewed-by: @altaywtf * fix(plugins): accept clawhub uninstall specs * test(auth): align device scope expectations (openclaw#53151) * fix: prevent delivery-mirror re-delivery and raise Slack chunk limit (openclaw#45489) Merged via squash. Prepared head SHA: c7664c7 Co-authored-by: theo674 <261068216+theo674@users.noreply.github.com> Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com> Reviewed-by: @altaywtf * Infra: tighten shell-wrapper positional-argv allowlist matching (openclaw#53133) * Infra: tighten shell carrier allowlist matching * fix(security): tighten shell carrier allowlist matcher * fix: generalize api_error detection for fallback model triggering (openclaw#49611) Co-authored-by: Ayush Ojha <7945279+ayushozha@users.noreply.github.com> Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com> * feat(modelstudio): add standard (pay-as-you-go) DashScope endpoints for Qwen (openclaw#43878) Add Standard API Key auth methods for China (dashscope.aliyuncs.com) and Global/Intl (dashscope-intl.aliyuncs.com) pay-as-you-go endpoints alongside the existing Coding Plan (subscription) endpoints. Also updates group label to 'Qwen (Alibaba Cloud Model Studio)' and fixes glm-4.7 -> glm-5 in Coding Plan note messages. Co-authored-by: wenmeng zhou <wenmengzhou@users.noreply.github.com> * Release: privatize macOS publish flow (openclaw#53166) * fix(diagnostics): redact credentials from cache-trace diagnostic output Refs openclaw#53103 * Release: document manual macOS asset upload (openclaw#53178) * Release: document manual macOS asset upload * Release: document macOS smoke-test mode * docs(changelog): reorder release highlights * test(whatsapp): stabilize login coverage in shared workers * test(whatsapp): preserve session exports in login coverage * test(whatsapp): preserve media test module exports * test(whatsapp): preserve harness session exports * fix(ci): stabilize whatsapp extension checks * test: make update-cli checkout path assertion platform-safe * fix(auth): prevent stale auth store reverts (openclaw#53211) * Doctor: prune stale plugin allowlist and entry refs (openclaw#53187) Signed-off-by: sallyom <somalley@redhat.com> * test: stabilize test isolation * test: update command coverage * test: expand gemini live transcript stripping * test: fix update-cli default path assertion * chore(sre:PLA-920): adopt upstream sync changes * fix(sre:PLA-920): align branch with adopted upstream tree * build(sre:PLA-920): refresh dist artifacts * test(sre:PLA-920): align incident-format expectations --------- Signed-off-by: sallyom <somalley@redhat.com> Co-authored-by: Vincent Koc <vincentkoc@ieee.org> Co-authored-by: Peter Steinberger <steipete@gmail.com> Co-authored-by: Rick_Xu <rick_xu@asus.com> Co-authored-by: rcrick <23069968+rcrick@users.noreply.github.com> Co-authored-by: frankekn <4488090+frankekn@users.noreply.github.com> Co-authored-by: oliviareid-svg <oliviareid@visionclaw.dev> Co-authored-by: oliviareid-svg <269669958+oliviareid-svg@users.noreply.github.com> Co-authored-by: Frank <vibespecs@gmail.com> Co-authored-by: scoootscooob <zhentongfan@gmail.com> Co-authored-by: ruochen <wangrui@ruochen.email> Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Frank Yang <frank.ekn@gmail.com> Co-authored-by: artwalker <44759507+artwalker@users.noreply.github.com> Co-authored-by: RichardCao <create0818@163.com> Co-authored-by: create <create@createdeMacBook-Pro.local> Co-authored-by: RichardCao <RichardCao@users.noreply.github.com> Co-authored-by: liuyang <liuyang@hkgai.org> Co-authored-by: Ayaan Zaidi <hi@obviy.us> Co-authored-by: Matt Van Horn <mvanhorn@users.noreply.github.com> Co-authored-by: Penchan <penchan@penchan.co> Co-authored-by: Penchan <5032148+p3nchan@users.noreply.github.com> Co-authored-by: Julia Bush <j.elizabethbush@gmail.com> Co-authored-by: Bakhtier Sizhaev <108124494+bakhtiersizhaev@users.noreply.github.com> Co-authored-by: Бахтиер Сижаев <bkh@MacBook-Air.local> Co-authored-by: wangchunyue <80630709+openperf@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> Co-authored-by: evann <newmanevanv@myyahoo.com> Co-authored-by: Robin Waslander <r.waslander@gmail.com> Co-authored-by: Sathvik Veerapaneni <98241593+Sathvik-Chowdary-Veerapaneni@users.noreply.github.com> Co-authored-by: Nimrod Gutman <nimrod.gutman@gmail.com> Co-authored-by: Luke <92253590+ImLukeF@users.noreply.github.com> Co-authored-by: scoootscooob <167050519+scoootscooob@users.noreply.github.com> Co-authored-by: Jamil Zakirov <jamil@zakirov.com> Co-authored-by: TheRipper <144421782+DavidNitZ@users.noreply.github.com> Co-authored-by: Quinn H. <quinnhou@foxmail.com> Co-authored-by: Ubuntu <ubuntu@ip-172-26-10-234.us-west-2.compute.internal> Co-authored-by: Val Alexander <68980965+BunsDev@users.noreply.github.com> Co-authored-by: betoblair <alberto.farah.b@gmail.com> Co-authored-by: haroldfabla2-hue <229189334+haroldfabla2-hue@users.noreply.github.com> Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com> Co-authored-by: Altay <altay@uinaf.dev> Co-authored-by: theo674 <theo@takethesis.com> Co-authored-by: theo674 <261068216+theo674@users.noreply.github.com> Co-authored-by: Ayush Ojha <ayushojzha@gmail.com> Co-authored-by: Ayush Ojha <7945279+ayushozha@users.noreply.github.com> Co-authored-by: George Zhang <georgezhangtj97@gmail.com> Co-authored-by: wenmeng zhou <wenmengzhou@users.noreply.github.com> Co-authored-by: Onur <onur@textcortex.com> Co-authored-by: Sally O'Malley <somalley@redhat.com>
…52913) * fix(channels): ship official channel catalog (openclaw#52838) * fix(release): keep shipped bundles in npm tar (openclaw#52838) * build(release): fix rebased release-check helpers (openclaw#52838)
…52913) * fix(channels): ship official channel catalog (openclaw#52838) * fix(release): keep shipped bundles in npm tar (openclaw#52838) * build(release): fix rebased release-check helpers (openclaw#52838)
…52913) * fix(channels): ship official channel catalog (openclaw#52838) * fix(release): keep shipped bundles in npm tar (openclaw#52838) * build(release): fix rebased release-check helpers (openclaw#52838)
Summary
Describe the problem and fix in 2–5 bullets:
2026.3.22stripped previously shipped optional bundled extension metadata anddist/control-uifrom the npm tarball, which left upgraded packaged installs with missing channel discovery and missing UI assets.Unknown channel: whatsapp, lose auto-install discovery for install-on-demand channels, and end up with missing Control UI assets after upgrade.dist/control-ui/index.htmlplus optional bundled extension manifests inrelease-check, and makerelease:checkbuild Control UI before validating the pack.@openclaw/whatsapp, does not change plugin install semantics beyond restoring discovery and shipped upgrade surfaces, and does not force optional bundles for lanes that explicitly opt out withOPENCLAW_INCLUDE_OPTIONAL_BUNDLED=0.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
dist/control-uiand the previously shipped optional bundled extension metadata needed for upgrade continuity.Security Impact (required)
Yes/No) NoYes/No) NoYes/No) NoYes/No) NoYes/No) NoYes, explain risk + mitigation:Repro + Verification
Environment
OPENCLAW_STATE_DIRfor packaged-install reproSteps
openclaw@2026.3.22into a clean temporary directory.openclaw channels add --channel whatsappor inspect the packaged tarball contents.dist/control-uiare missing from the shipped artifact.Expected
Actual
Evidence
Attach at least one:
Human Verification (required)
What you personally verified (not just CI), and how:
pnpm checkpnpm buildpnpm test -- test/official-channel-catalog.test.tspnpm test -- src/channels/plugins/plugins-core.test.tspnpm test -- src/plugins/copy-bundled-plugin-metadata.test.tspnpm test -- test/release-check.test.tsnode scripts/stage-bundled-plugin-runtime-deps.mjspnpm ui:buildnode --import tsx scripts/release-check.tsnpm pack --dry-run --json --ignore-scriptsincludesdist/channel-catalog.json,dist/control-ui/index.html, and the optional bundled extension manifests foracpx,diagnostics-otel,diffs,googlechat,matrix,memory-lancedb,msteams,nostr,tlon,twitch,whatsapp, andzalouserpluginIdOPENCLAW_INCLUDE_OPTIONAL_BUNDLED=0still disables optional bundles for size-sensitive lanesrelease-checkfails when required shipped paths are missing@openclaw/whatsapppackagepnpm release:checkwrapper is still blocked earlier by existing config baseline drift on currentmain; the pack-validation portion was run directly and passedReview Conversations
If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.
Compatibility / Migration
Yes/No) YesYes/No) NoYes/No) NoFailure Recovery (if this breaks)
2f1f4100ec, or setOPENCLAW_INCLUDE_OPTIONAL_BUNDLED=0on a size-sensitive packaging lane that must opt out explicitlyscripts/lib/optional-bundled-clusters.mjs,scripts/release-check.ts,package.jsondist/control-ui/index.html, missing optional bundled extension manifests, or pack size growing materially beyond the new176 MiBbudgetRisks and Mitigations
List only real risks for this PR. Add/remove entries as needed. If none, write
None.176 MiB, while remaining well below the2026.3.12213.6 MiBregression guardrail, andrelease-checkstill enforces the budget.OPENCLAW_INCLUDE_OPTIONAL_BUNDLED=0.