Fixes#58409 - Heartbeat system causes silent session reset leading to user data loss.
The issue occurred when automated system events (heartbeat, cron-event, exec-event)
triggered the session initialization logic, which evaluated session freshness based on
idle/daily reset policies. Stale sessions were reset, causing complete context loss.
Changes:
- Detect system event providers (heartbeat, cron-event, exec-event) in initSessionState
- Force freshEntry=true for system events to skip reset policy evaluation
- Add comprehensive test coverage for heartbeat no-reset behavior
This ensures automated check-ins preserve session continuity and never cause
accidental data loss.
* routing: support wildcard peer bindings (peer.id="*") for multi-agent routing
Bindings with `peer: { kind: "direct", id: "*" }` were treated as a literal
peer ID "*" instead of a wildcard. This caused the binding to be indexed
exclusively in the byPeer map under key "direct:*", which never matches
actual incoming peer IDs like "direct:12345678". The binding silently fell
through to the default agent ("main"), breaking multi-agent setups that use
wildcard peer constraints to route all DMs on a named account to a specific
agent.
Add a "wildcard-kind" peer constraint state that restricts on chat type
(direct/group/channel) without requiring an exact peer ID match. Wildcard
peer bindings now fall through to the byAccount/byChannel index tiers and
correctly match via matchesBindingScope with kind-only filtering.
Resolves#58546
Made-with: Cursor
* routing: add dedicated binding.peer.wildcard tier for clarity
Address Greptile feedback: wildcard-peer bindings now report
matchedBy: "binding.peer.wildcard" instead of "binding.account",
making logs/debugging clearer for operators.
- Add byPeerWildcard index bucket
- Add binding.peer.wildcard tier between peer.parent and guild+roles
- Update tests to expect the new matchedBy value
Made-with: Cursor
* fix: prevent infinite retry loop when live session model switch blocks failover (#58466)
* fix: remove unused resolveOllamaBaseUrlForRun import after rebase
* fix(media): resolve relative MEDIA: paths against agent workspace dir
* fix(agents): remove stale ollama compat import
* fix(media): preserve workspace dir in outbound access
Instead of exponential backoff guesses on Codex 429, probe the WHAM
usage API to determine real availability and write accurate cooldowns.
- Burst/concurrency contention: 15s circuit-break
- Genuine rate limit: proportional to real reset time (capped 2-4h)
- Expired token: 12h cooldown
- Dead account: 24h cooldown
- Probe failure: 30s fail-open
This prevents cascade lockouts when multiple agents share a pool of
Codex profiles, and avoids wasted retries on genuinely exhausted
profiles.
Closes#26329, relates to #1815, #1522, #23996, #54060
Co-authored-by: ryanngit <ryanngit@users.noreply.github.com>
config.patch unconditionally writes the config file and sends SIGUSR1
even when diffConfigPaths detects zero changed paths. This causes a
full gateway restart (~10s downtime, all SSE/WebSocket connections
dropped) on every control-plane config.patch call, even when the
config is identical — e.g. a model hot-apply that doesn't change any
gateway.* paths.
Fix: when changedPaths is empty, return early with `noop: true`
without writing the file or scheduling SIGUSR1. The validated config
is still returned so the caller knows the current state.
This lets control-plane clients safely call config.patch for
idempotent updates without triggering unnecessary restarts.
Add toolsAllow field to cron agent-turn payloads, enabling users to
restrict which tool schemas are sent to the model for a given cron job.
When --tools is set:
- Only listed tools are included in the provider request
- promptMode is forced to 'minimal' (strips skills catalog, reply tags,
heartbeat, messaging, docs, memory, model aliases, silent replies)
- Dramatically reduces input tokens for small local models (~16K to ~800)
CLI surface:
- openclaw cron add --tools exec,read,write
- openclaw cron edit <id> --tools exec
- openclaw cron edit <id> --clear-tools (remove allow-list)
Closes#58435
Co-authored-by: andyk-ms <andyk-ms@users.noreply.github.com>
Allow setting provider params (e.g. cacheRetention) once at the
agents.defaults level instead of repeating them per-model. The merge
order is now: defaults.params -> defaults.models[key].params ->
list[agentId].params.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
resolveTrustedHttpOperatorScopes() returns [] when the x-openclaw-scopes
header is absent, even for trusted requests (--auth none). This causes
403 "missing scope: operator.write" on /v1/chat/completions.
Root cause: src/gateway/http-utils.ts:138-140. PR #57783 (f0af18672)
replaced the old resolveGatewayRequestedOperatorScopes which had an
explicit fallback to CLI_DEFAULT_OPERATOR_SCOPES when no header was
present. The new function treats absent header the same as empty header
— both return [].
Fix: distinguish absent header (undefined → return defaults) from empty
header ("" → return []). Trusted clients without an explicit scope
header get the default operator scopes, matching pre-#57783 behavior.
Closes#58357
Signed-off-by: HCL <chenglunhu@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>