Adds an `on-exit` cron schedule kind: a job fires once when a watched command/process
exits, via gateway ProcessSupervisor exit watchers. Covers CLI (`--on-exit`/`--on-exit-cwd`),
tool/protocol schema, RPC list-filter, Control UI + macOS read-only display, SQLite
round-trip, and origin-aware wake routing. Restart-safe one-shot (persists completion
before firing); platform-aware shell; bounded watched-command execution.
Squashed from 22 iterative commits for a clean rebase onto current main.
Adds to the type so plugin
commands that handle their own transport delivery (e.g. via Telegram Bot API
directly with retry logic, IPv4 forcing, etc.) can signal the channel adapter
to skip the fallback reply.
When a plugin command handler returns , the Telegram
native command dispatcher now:
1. Cleans up any progress placeholder
2. Returns early without sending the "No response generated. Please try again." fallback
Includes detailed JSDoc for the new flag explaining its use
for plugin commands that deliver their own responses via channel-native APIs.
Fixes#80756
Preserve structured tool-result replay text across provider transports while keeping provider-facing redaction and Anthropic media ordering intact.
Thanks @snowzlmbot!
* feat(openai): add GPT-5.6 series support
* docs: refresh map for GPT-5.6
* fix(openai): preserve GPT-5.6 thinking metadata
* fix(codex): sync managed app server version
* fix(codex): sync managed app server version
* fix(openai): account for GPT-5.6 cache writes
---------
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
* 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>