* fix(plugins): plugin loggers drop writes after the log level is raised at runtime
The plugin runtime logging facade captured a single tslog child logger per
getChildLogger() call. tslog snapshots a sublogger's min level at creation, so a
long-lived plugin logger (e.g. a channel monitor that runs for the whole gateway
session) kept dropping debug/verbose writes after the log level was raised at
runtime, even though shouldLogVerbose()/isFileLogLevelEnabled() reported the new
level. This made channels like Mattermost go dark while core subsystem loggers
(which re-resolve per emit) kept logging.
Resolve the child logger per call so it always reflects the current level, with a
cheap isFileLogLevelEnabled pre-gate (skipped for explicit overrides) to avoid
building a sublogger when the level is disabled. Fixes every channel that holds a
long-lived monitor logger (mattermost, matrix, msteams, irc, nextcloud-talk) at
the facade boundary with no plugin-code changes.
* test(plugins): type runtime log-level mock
---------
Co-authored-by: Alex Knight <15041791+amknight@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(status): distinguish runtime-loaded plugins from installed inventory
/status plugins merged disk-scan plugin records with the active runtime
registry, so the detailed Loaded: list could include plugins that were
installed/config-enabled but never loaded at runtime. Record the runtime-
loaded plugin ids on the health snapshot, carry them through the merge, and
make Loaded: reflect runtime-confirmed plugins; show installed/discovered
plugins that are not active as a neutral "Installed (not active)" inventory
line rather than an error.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(status): include pinned runtime registries in loaded ids
Build runtimeLoadedPluginIds from all live runtime registry surfaces via
collectLivePluginRegistries() (active plus any pinned channel / http-route /
session-extension registry) and render Loaded: from that id set directly, so a
plugin live only through a pinned surface still counts as loaded instead of
being dropped or misreported as "Installed (not active)".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* [AI] fix(plugins): recognize document-extractors as a capability kind in inspect-shape
PluginCapabilityKind did not include "document-extractors", causing
plugins that declare contracts.documentExtractors (like document-extract)
to show capabilityCount=0 and shape="non-capability" in plugins inspect.
Add "document-extractors" to PluginCapabilityKind and read from
plugin.contracts.documentExtractors in buildPluginCapabilityEntries().
Related to #91539
* [AI] test(plugins): add document-extractors shape contract coverage
Add a test case verifying that plugins declaring
contracts.documentExtractors are classified as
plain-capability shape with capabilityCount=1
and capabilities including the document-extractors kind.
Addresses ClawSweeper P2 review finding on PR #91597.
* [AI] chore: rebase on main to refresh CI
* test(plugins): fold extractor into shape matrix
---------
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
* fix(plugins): stop ClawHub version install from inheriting latest compatibility
When installing a specific older version of a ClawHub plugin, the
compatibility check fell back to the package-level compatibility
metadata when the version-specific response lacked it. The package-level
field reflects the latest version's requirements, so installing e.g.
version 2026.6.8 would incorrectly require OpenClaw >= 2026.6.10.
Remove the fallback to package-level compatibility in
resolveCompatiblePackageVersion(). If a version's artifact response has
no compatibility data, treat it as having no restrictions rather than
inheriting the latest version's constraints.
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: IsaiahStapleton <istaplet@redhat.com>
* fix(plugins): narrow compatibility fallback to latest version only
Preserve package-level compatibility enforcement for unpinned/latest
ClawHub installs when the version response omits compatibility data.
Only suppress the fallback for older pinned versions where the
package-level metadata reflects the latest version's requirements.
Add regression test proving unpinned latest installs still reject
incompatible hosts via the package-level compatibility guard.
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: IsaiahStapleton <istaplet@redhat.com>
* fix(plugins): recover version-specific compatibility from version endpoint
When the artifact endpoint returns sparse metadata (no compatibility)
for a pinned older version, fetch the version endpoint to get the real
version-specific compatibility data. This preserves compatibility
enforcement for pinned versions instead of treating sparse artifact
metadata as unrestricted.
The fallback chain is now: artifact compatibility -> version endpoint
compatibility -> package-level compatibility (latest only).
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: IsaiahStapleton <istaplet@redhat.com>
* fix(plugins): fail closed when version compatibility recovery fails
When the artifact endpoint returns sparse metadata and the version
endpoint is unavailable (transient 500, network failure, etc.), return
the error instead of proceeding with no compatibility checks. This
prevents incompatible plugins from installing when metadata recovery
fails.
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: IsaiahStapleton <istaplet@redhat.com>
---------
Signed-off-by: IsaiahStapleton <istaplet@redhat.com>
* fix(providers): bound self-hosted discovery JSON reads
discoverLlamaCppRuntimeContextTokens and discoverOpenAICompatibleLocalModels
parsed their HTTP responses via an unbounded await response.json(). Self-hosted
provider base URLs are user-supplied and untrusted (an endpoint reachable via
SSRF could stream an unbounded JSON body), so a hostile or buggy endpoint could
drive the setup wizard into OOM.
Route both reads through the shared byte-bounded reader (readResponseWithLimit
from @openclaw/media-core) under a single 4 MiB cap before JSON.parse, mirroring
the bound-stream hardening landed for Anthropic error bodies. Overflow cancels
the stream and is swallowed by the existing discovery error handling, so a
capped endpoint degrades gracefully (returns [] / skips the runtime context
probe) instead of buffering the whole body.
* tune self-hosted discovery cap
Signed-off-by: sallyom <somalley@redhat.com>
---------
Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: sallyom <somalley@redhat.com>
Update OpenClaw ClawHub docs and user-facing copy for canonical owner-qualified skill routes.\n\nEvidence:\n- pnpm docs:list\n- pnpm test src/plugins/clawhub.test.ts src/cli/plugins-cli.install.test.ts src/gateway/server-methods/skills.clawhub.test.ts ui/src/ui/views/skills.test.ts\n- pnpm exec oxfmt --check --threads=1 docs/clawhub/cli.md docs/clawhub/publishing.md docs/cli/skills.md docs/help/faq.md docs/start/showcase.md docs/tools/creating-skills.md docs/tools/skills.md src/gateway/server-methods/skills.clawhub.test.ts src/plugins/clawhub.test.ts src/plugins/clawhub.ts ui/src/ui/views/skills.test.ts\n- git diff --check\n- exact-head hosted CI passed for 8530374388d8a73235b2ac8444b95a4a4c7d0f1c\n\nNote: repo-native scripts/pr prepare-run was attempted; local broad pnpm test was stopped after unrelated existing failures in agent/media/provider shards, while hosted exact-head CI and targeted ClawHub route/copy validation were green.
* fix(plugins): make empty-allowlist warning actionable for first-time users
* fix(plugins): make empty-allowlist warnings actionable
* fix(plugins): make empty-allowlist warnings actionable
* fix(plugins): make empty-allowlist actionable for new users
---------
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
* plugins: clarify allowlist warning when entries don't match discovered ids
When plugins.allow contains entries that do not match any discovered
plugin id (for example a channel id like feishu instead of the real
plugin id openclaw-lark), stop emitting the misleading "plugins.allow
is empty" warning. Emit a specific mismatch warning that lists the
unknown allow entries alongside the discovered plugin ids and points
users at the plugin id rather than a channel id or npm package name.
Refs #68352
* plugins: treat bundled plugin ids as valid allow entries
Codex P2 on #68389: warnWhenAllowlistIsOpen computed allowHasMatch
against the auto-discoverable (workspace + global) subset only, so
a legitimate bundled-only allowlist like plugins.allow=['telegram']
would trip the new mismatch warning whenever any non-bundled plugin
happened to be discoverable alongside it.
Compare allow entries to every discovered plugin id (bundled +
workspace + global) for both the short-circuit and the unmatched-
entries computation. The warning text stays scoped to non-bundled
auto-discoverable plugins; we just stop flagging bundled ids as
'does not match any discovered plugin ids'. Add a regression test
that covers the bundled-only allowlist + non-bundled workspace
plugin combination.
Refs #68352
* chore: drop release-owned CHANGELOG entry (AGENTS.md: changelog is release-generated)
* plugins: clarify allowlist warning when entries do not match plugin ids
---------
Co-authored-by: Sean Sun <lyfuci11@gmail.com>
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>