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() records pendingReason
  • 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:

  1. Global heartbeat switch must be enabled
  2. Heartbeat config must be enabled (every > 0)
  3. activeHours check must pass (if configured)
  4. Main queue must be idle
  5. HEARTBEAT.md must 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_OK is treated as an acknowledgement token
  • ackMaxChars controls 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/recipient
  • target: "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 immediately
  • next-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 immediately
  • POST /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:

  1. src/agents/bash-tools.exec.ts: enqueue event and request heartbeat
  2. src/gateway/server-node-events.ts: node exec.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.transcript to gateway node session

5.4 Android

VoiceWakeManager.kt + NodeRuntime.kt:

  • SpeechRecognizer with partial results
  • Command extraction by regex
  • VoiceWakeMode controls listening scope (off/foreground/always)
  • Sends agent.request with deliver:false

6. End-to-End Abstract Flow

text
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)

  1. System event queue is in-memory with fixed capacity. Evidence: risk of event loss under sustained spikes, and restart clears queue state.
  2. Only one pendingReason is preserved in wake coalescing. Evidence: concurrent triggers can overwrite reason semantics.
  3. Busy main lane can repeatedly postpone heartbeat work. Evidence: active tasks may be delayed for long periods during heavy traffic.
  4. 24-hour dedupe may suppress reminders that are intentionally repetitive. Evidence: same text can be dropped even when periodic re-alerting is desired.
  5. Voice wake config is global-file scoped. Evidence: it lacks fine-grained per-agent/per-channel/per-user override.

Table of Contents