Skill: dispo-orphan-recovery

Scans SalesMsg inbound messages for conversations with no pending draft and generates Kimi drafts for all of them, posting @aurora notes so Henry can review and approve immediately.

Trigger

User says any of:

  • /dispo-orphan-recovery
  • “check for missed drafts”
  • “messages with no draft”
  • “unanswered inbounds”
  • “orphan recovery”
  • “drafts for missed messages”
  • “generate drafts for unanswered”
  • “salesmsg messages without a response”

Usage

# Default: last 24h, live
node /home/opsadmin/.openclaw/workspace/scripts/dispo-orphan-recovery.js
 
# Custom window
node /home/opsadmin/.openclaw/workspace/scripts/dispo-orphan-recovery.js --since-hours 6
 
# Dry run — scan only, no writes, no LLM calls
node /home/opsadmin/.openclaw/workspace/scripts/dispo-orphan-recovery.js --dry-run
 
# Single phone recovery
node /home/opsadmin/.openclaw/workspace/scripts/dispo-orphan-recovery.js --phone +16198551646
 
# JSON output for automation
node /home/opsadmin/.openclaw/workspace/scripts/dispo-orphan-recovery.js --json --dry-run

What it does

  1. Queries salesmsg_inbox for inbound messages (non-note) in the last N hours
  2. Deduplicates to one (most recent) message per phone
  3. Checks message_approvals for any pending/approved record for those phones (48h window)
  4. For each phone with NO draft: calls generateResponse (Kimi via response-generator.js)
  5. Inserts pending message_approvals row with full telemetry in ai_context
  6. Posts @aurora internal note in the SalesMsg conversation with draft + confidence score
  7. Reports results — queued count, errors, dry-run list

Output

Each orphan gets:

  • A message_approvals row with status: 'pending' and source: 'dispo-orphan-recovery' in ai_context
  • An internal note in the SalesMsg conversation (visible to Henry immediately)
  • The approval poller picks it up and sends within 5 minutes if auto-send is enabled

Root cause context

The groundedResult is not defined bug (scoping error in salesmessage-handler-v4-complete.js, fixed 2026-04-29) caused PM2 crashes during draft generation, leaving 12+ inbounds without drafts. This skill is the recovery mechanism AND an ongoing safety net.

When to run on-demand

  • After a PM2 restart (handler loses in-memory state, may have missed messages during restart window)
  • After any deploy to salesmsg-webhook service
  • When Henry notices a buyer conversation has no draft in SalesMsg
  • Morning check: --since-hours 8 to cover overnight

Scheduled sweep (optional)

To run automatically every hour as a safety net:

# Check status
systemctl --user status dispo-orphan-recovery.timer
 
# Install (if not yet running)
# Service + timer files should be created if Henry wants this automated

Files

  • Script: /home/opsadmin/.openclaw/workspace/scripts/dispo-orphan-recovery.js
  • Tables read: salesmsg_inbox, message_approvals
  • Tables written: message_approvals
  • Token used: data/salesmessage-oauth.json (Henry’s token — required for notes; Aurora’s token can’t post notes)

Limitations

  • Processes 2 orphans concurrently (rate-limit friendly)
  • Skips phones already covered by a pending/approved draft (safe to re-run)
  • If Kimi returns empty draft, logs a warning and skips that phone (does not insert blank approval)