OpenClaw Active Wake-Up Mechanism Design
OpenClaw Active Wake-Up Mechanism Design
This article summarizes how OpenClaw triggers agent behavior without waiting for a manual user message. Based on the codebase, active wake-up has two major paths:
- Gateway-side wake-up: system events + heartbeat + cron + webhook + async exec completion
- Device-side wake-up: wake word / push-to-talk on macOS, iOS, and Android
1. Gateway Side: System Events + Heartbeat
1.1 System Event Queue
src/infra/system-events.ts is the core carrier for proactive triggers.
- Events are bucketed by
sessionKey(required, non-empty) - Queue is in-memory only, not persisted
- Consecutive duplicate text is skipped
- Events are drained and cleared during heartbeat execution
1.2 Heartbeat Wake Scheduling and Coalescing
src/infra/heartbeat-wake.ts adds a merge/throttle layer.
requestHeartbeatNow()recordspendingReason- Default coalescing window is 250ms
- If already running, new wake requests are marked
scheduled - If main lane is busy, wake execution is retried with delay
1.3 Heartbeat Run Conditions
src/infra/heartbeat-runner.ts applies strict preconditions:
- Global heartbeat switch must be enabled
- Heartbeat config must be enabled (
every > 0) activeHourscheck must pass (if configured)- Main queue must be idle
HEARTBEAT.mdmust not be effectively empty (except some exec-event paths)
Default heartbeat prompt (src/auto-reply/heartbeat.ts) asks the model to read HEARTBEAT.md and return HEARTBEAT_OK when no action is needed.
1.4 HEARTBEAT_OK and Output Suppression
Heartbeat output is filtered before delivery:
HEARTBEAT_OKis treated as an acknowledgement tokenackMaxCharscontrols when short "OK + tiny text" payloads are suppressed- Visibility policy (
showOk,showAlerts,useIndicator) controls whether any message is sent - Repeated identical heartbeat text is deduped (24h window)
1.5 Delivery Target Resolution
resolveHeartbeatDeliveryTarget chooses outbound target:
target: "last"-> last active channel/recipienttarget: "none"-> run only, do not send- explicit
target + to-> fixed channel and recipient
2. Gateway Side: Cron Wake-Up
src/cron/* is the persistent scheduler.
2.1 Two Execution Modes
- Main session (
systemEvent): enqueue event, then heartbeat handles it - Isolated session (
agentTurn): run isolated turn (cron:<jobId>), summarize back to main session
wakeMode can be:
now: trigger heartbeat immediatelynext-heartbeat: only request next heartbeat cycle
2.2 Completion and Status
When wakeMode: "now" and runHeartbeatOnce is available, cron waits for completion and marks job status as ok, skipped, or error.
3. Gateway Side: Webhook Wake-Up
src/gateway/server/hooks.ts and docs/automation/webhook.md expose proactive wake endpoints.
POST /hooks/wake: enqueue system event, optionally wake immediatelyPOST /hooks/agent: run isolated task, summarize to main session, optionally wake
Both flows reuse system-events + heartbeat.
4. Async Exec Completion Trigger
When a background command finishes, OpenClaw wakes heartbeat:
src/agents/bash-tools.exec.ts: enqueue event and request heartbeatsrc/gateway/server-node-events.ts: nodeexec.finished-> enqueue event and request heartbeat
Exec-event mode uses a dedicated prompt (EXEC_EVENT_PROMPT) to avoid returning only HEARTBEAT_OK.
5. Voice Wake (Device-Side)
Voice wake bypasses heartbeat and directly triggers agent command execution.
5.1 Global Trigger Config
src/infra/voicewake.ts + src/gateway/server-methods/voicewake.ts:
- Config stored at
~/.openclaw/settings/voicewake.json - Default wake words:
openclaw,claude,computer - APIs:
voicewake.get,voicewake.set,voicewake.changed
5.2 macOS
apps/macos/Sources/OpenClaw/VoiceWakeRuntime.swift:
- Uses
SFSpeechRecognizer + SwabbleKit - Applies silence/hard-stop capture window
- Uses cooldown after trigger
- Sends transcript via
GatewayConnection.shared.sendAgent(...)
VoicePushToTalk temporarily pauses wake-word recognition to avoid mic contention.
5.3 iOS
apps/ios/Sources/Voice/VoiceWakeManager.swift + NodeAppModel.swift:
- Real-time recognition with
AVAudioEngine + SFSpeechRecognizer - Wake-word extraction via
WakeWordGate - Sends
voice.transcriptto gateway node session
5.4 Android
VoiceWakeManager.kt + NodeRuntime.kt:
SpeechRecognizerwith partial results- Command extraction by regex
VoiceWakeModecontrols listening scope (off/foreground/always)- Sends
agent.requestwithdeliver:false
6. End-to-End Abstract Flow
External trigger / schedule / exec completion
-> enqueueSystemEvent(text, sessionKey)
-> requestHeartbeatNow()
-> runHeartbeatOnce()
-> prompt + HEARTBEAT.md
-> deliver / skip / dedupe
Voice wake (macOS/iOS/Android)
-> local speech recognition
-> wake-word hit + command extraction
-> node event / agent request
-> gateway agentCommand()
7. Potential Issues (Code Review)
- System event queue is in-memory with fixed capacity. Evidence: risk of event loss under sustained spikes, and restart clears queue state.
- Only one
pendingReasonis preserved in wake coalescing. Evidence: concurrent triggers can overwrite reason semantics. - Busy main lane can repeatedly postpone heartbeat work. Evidence: active tasks may be delayed for long periods during heavy traffic.
- 24-hour dedupe may suppress reminders that are intentionally repetitive. Evidence: same text can be dropped even when periodic re-alerting is desired.
- Voice wake config is global-file scoped. Evidence: it lacks fine-grained per-agent/per-channel/per-user override.