An untrusted channel plugin cannot open its durable ingress queue, but the shared
ingress monitor resolved the queue lazily, so start() armed the poll timer anyway and
every tick re-invoked the throwing factory. On a production gateway that produced one
INFO-level "slack ingress drain failed" line per second for 26+ hours while Slack
reported connected and healthy and every inbound event was silently dropped.
Open the queue before arming the poll timer so an unusable monitor fails channel start
through the caller instead of spinning. Also report the actual denied capability:
openChannelIngressQueue and openSyncKeyedStore both announced themselves as
openKeyedStore, and the message named neither the plugin nor its origin.
The health monitor treated an account sleeping between its own auto-restart
attempts as "stopped" and force-restarted it. That start booted nothing (the
channel supervisor still owned the account task), but the accompanying
resetRestartAttempts wiped the retry ladder, so a crash-looping channel
restarted at attempt 1 every cooldown and could never reach
"giving up after 10 restart attempts".
ChannelManager now reports isAutoRestartScheduled for the supervisor's backoff
sleep and replacement start, and the health monitor defers to it instead of
restarting, resetting attempts, or spending its cooldown/hourly budget. The
monitor stays the last restart owner once the supervisor gives up.
Live evidence (clawmac gateway logs, 2026-07-27/28): 198 "restarting
(reason: stopped)" lines, zero give-ups, attempt ladders truncated at 8/10.
Related: #110802, #111572
* feat(config): give shared channel settings help text
* fix(ui): space the settings tiers and stop repeating help on list items
* feat(ui): link each channel detail to its docs page
* test(ui): mirror production channel hints in the mock dev server
* docs: note the shared channel help inheritance for plugin authors
* fix(config): let a channel suppress shared help with an empty string
* chore(config): regenerate bundled channel metadata for the new WhatsApp hints
* fix(ci): check bundled channel metadata locally
* chore(release): keep changelog release-owned
* chore(release): keep changelog release-owned
* fix(ui): keep shared channel help contract-neutral
* feat(agents): add per-run stats to embedded agent run meta
Adds codeModeEngaged, assistantTurns, bridgeCalls, and costUsd to
EmbeddedAgentRunMeta.agentMeta and mirrors them on the agent exec --json
envelope. Code-mode engagement is stamped from the tool-surface truth,
round trips accumulate across attempts beside usage, bridge counts come
from the run's tool-search catalog counters, and cost reuses the shared
model pricing helpers (cache tiers included, omitted without cost data).
* fix(agents): accumulate bridge call counts across run attempts
Attempt cleanup clears the per-attempt tool-search catalog, so retries and
fallbacks discarded earlier bridge counts. Fold each attempt's bridgeCalls
into the run accumulator beside assistantTurns and stamp the cumulative
totals into agentMeta, matching the documented per-run contract.
* refactor(channels): own account status in one closed state machine
* fix(gateway): stop storing derived reasons and configured in channel runtime
* fix(channels): separate linkage from configuration in whatsapp and zalo personal
* fix(cli): render channel state reasons apart from runtime failures
* refactor(channels): keep account-state projection internal to its owner
A user-home app-server keeps its native Codex account, and OpenClaw verified only
half of that contract. A subscription route was checked against the native
account, but a Platform (API-key) route was not: an operator signed in to Codex
with a ChatGPT subscription who selected an API-billed model silently spent their
plan. Both directions of the same billing boundary now share one account/read
check. An absent account is left alone, since a native home may serve a custom
model provider that reports no OpenAI account at all.
The home-scope rule behind #114397 was also expressed four times: two
prepared-auth call sites spread it in conditionally from raw plugin config, two
others read resolved start options, and the supervision connection applied its
own default inline. resolveCodexAppServerHomeScope now owns it, and homeScope is
a required argument of resolveCodexAppServerPreparedAuthHandoff, so a new call
site that forgets it fails to compile instead of failing every turn.
Docs dropped the stale user-home paragraph that still described the startup
failure #114397 removed and told operators to delete a working OpenAI profile.
* feat(plugins): deliver sessions.changed to plugin services
* docs: regenerate docs map
* refactor(clickclack): split reconcile scheduler and history formatting out of the discussion service
* style: format discussion service
* perf(logging): flush file transport asynchronously off the request path
* fix(ci): record flushLogger surface growth and drain before log reads
- +1 public export/callable budget with annotation: logger file-transport
flush for graceful shutdown drains; regenerate SDK API baseline
- three tests that read the log file right after logging now drain the
async file transport first (flushLogger) instead of racing the flusher
* test: drain async log transport before reading talk and request-trace logs
* test: drain async log transport in CLI logger file tests
* test: drop unused withTempDirSync import
* feat(plugins): mirror local coding sessions to a remote Beam receiver
* fix(plugins): satisfy static gates for the Beam mirror seam
* fix(plugins): import the config type from the narrow contracts subpath
* fix(plugins): require catalog consent and loopback-only plaintext for the Beam mirror
* fix(config): compaction.enabled is rejected as invalid config
Setting agents.defaults.compaction.enabled in openclaw.json made the
whole config fail to load with "agents.defaults.compaction: Invalid
input", so auto-compaction could not be turned off.
The runtime already reads the field (SettingsManager.getCompactionEnabled
returns settings.compaction?.enabled ?? true, and setCompactionEnabled
writes it), and the documented config example in
docs/reference/session-management-compaction.md already shows
"enabled: true". Only the zod schema was missing the key, and the
compaction object is .strict(), so the unknown key rejected the config.
Adds enabled to AgentDefaultsSchema and to AgentCompactionConfig.
Closes#110065
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(config): wire compaction enablement through runtime
Align the accepted compaction.enabled contract across runtime precedence, help, labels, docs, and generated schema baselines.
Co-authored-by: Zakaria Rahali <zakariarahali288@gmail.com>
* test(config): preserve omitted compaction setting
Document the reload lifecycle and lock in project-setting precedence when the OpenClaw config key is absent.
Co-authored-by: Zakaria Rahali <zakariarahali288@gmail.com>
* refactor(config): centralize compaction runtime override
* fix(config): preserve compaction safety guards
* fix(scripts): avoid ripgrep in merge fallback
* docs(config): refresh current main baseline
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* refactor(memory-core): remove orphaned shadow trials
* refactor(qqbot): migrate direct voice upload formats
Release note: QQBot configs using voiceDirectUploadFormats now migrate through openclaw doctor --fix to audioFormatPolicy.uploadDirectFormats at root and account scope. Existing nested uploadDirectFormats values win conflicts, and runtime reads only the nested policy.
* refactor(feishu): migrate tools base alias
Release note: Feishu configs using tools.base now migrate through openclaw doctor --fix to tools.bitable at root and account scope. Existing bitable values win conflicts, and runtime accepts only the canonical key.
* refactor(extensions): remove dead runtime state
* chore(config): refresh bundled channel metadata
* refactor(codex): keep realtime fallback type private
* test(ci): align QA compatibility scenario count
* feat(tools): per-model code-mode capability flags and auto master-switch tier
* feat(anthropic): flag claude-sonnet-4-6 as code-mode preferred
* revert(anthropic): drop claude-sonnet-4-6 from the code-mode preferred set
* test(anthropic): type the manifest compat field in the catalog contract test
* fix(anthropic): carry catalog compat onto hand-built forward-compat model rows
* refactor(slack): retire inline interactive directives
Release note: Slack configs using channels.slack.capabilities.interactiveReplies, including per-account settings, are removed by openclaw doctor --fix. Slack-only [[slack_buttons:...]] and [[slack_select:...]] markup is no longer parsed; emit typed presentation buttons and selects instead.
* docs: refresh Slack docs map
* test(ui): make channel settings renderable in the mock dev server
* fix(ui): hide advanced channel settings behind the shared advanced tier
* fix(ui): widen the tier renderer callback to lit's nothing
* fix(ui): keep the advanced collapse control on advanced-only channel forms
* docs: note the shared advanced tier on channel settings
* fix(gateway): reclaim startup migration leases whose owner process is gone
* fix(gateway): name a runnable command in crash-loop breaker suppression messages
* fix(gateway): scope the breaker recovery hint to the suppressed account
Reject recognizable POSIX and Windows shell source before QuickJS execution while preserving valid JavaScript, TypeScript, syntax errors, standard globals, and real hoisted bindings. Add real-worker regression coverage and adversarial cross-platform stress proof. Fixes#113069.