The generic "node command not allowed" error gives no indication of why the
command was rejected, making it hard to diagnose issues (e.g. running
`nodes notify` against a Linux node that does not declare `system.notify`).
Include the rejection reason and node platform in the error message so
callers can tell whether the command is not supported by the node, not in
the platform allowlist, or the node did not advertise its capabilities.
Fixes#24616
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit e3d74619bc)
* Config UI: add tag filters and complete schema help/labels
* Config UI: finalize tags/help polish and unblock test suite
* Protocol: regenerate Swift gateway models
* fix(hooks): suppress main session events for silent/delivered hook turns
When a hook agent turn returns NO_REPLY (SILENT_REPLY_TOKEN), mark the
result as delivered so the hooks handler skips enqueueSystemEvent and
requestHeartbeatNow. Without this, every Gmail notification classified
as NO_REPLY still injects a system event into the main agent session,
causing context window growth proportional to email volume.
Two-part fix:
- cron/isolated-agent/run.ts: set delivered:true when synthesizedText
matches SILENT_REPLY_TOKEN so callers know no notification is needed
- gateway/server/hooks.ts: guard enqueueSystemEvent + requestHeartbeatNow
with !result.delivered (addresses duplicate delivery, refs #20196)
Refs: https://github.com/openclaw/openclaw/issues/20196
* Changelog: document hook silent-delivery suppression fix
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix: preserve stored provider in resolveSessionModelRef for vendor-prefixed models
When an OpenRouter model with a vendor prefix (e.g. "anthropic/claude-haiku-4.5")
was successfully used and persisted to the session entry, the next call to
resolveSessionModelRef would re-parse the model string through parseModelRef,
which splits on the first slash and incorrectly extracts "anthropic" as the
provider — discarding the stored "openrouter" provider entirely. This caused
subsequent requests to attempt direct Anthropic API calls with an OpenRouter
API key, producing "credit balance too low" billing errors.
The fix trusts the explicitly stored modelProvider on the session entry and
skips parseModelRef re-parsing when a provider is already recorded. parseModelRef
is still used as a fallback when no provider is stored on the entry.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Changelog: add OpenRouter note for #22753
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
When a config file is written atomically (tmp → rename), chokidar can
fire an 'unlink' event for the temporary removal of the destination file
before the rename completes. runReload() would then call readSnapshot(),
which returns { exists: false, valid: true, config: {} } — an empty
config that looks valid — causing diffConfigPaths() to find many changes
and triggering an unnecessary SIGUSR1 restart.
The restarted gateway process then fails to find the config file (still
in the middle of the write) and enters a crash loop with:
'Missing config. Run openclaw setup...'
Fix: guard against exists=false before the existing valid=false check,
so mid-write snapshots are silently skipped rather than treated as a
config wipe.
Fixes#23321