Skip to content

Feature Request: Add skipOnHeartbeat Configuration Option #20

@rbutera

Description

@rbutera

Depends on: openclaw/openclaw#24585

Summary

Once the OpenClaw SDK exposes isHeartbeat in PluginHookAgentContext (see openclaw/openclaw#24585), Supermemory should add configuration options to optionally skip memory operations (recall and capture) during heartbeat runs. This will reduce API costs and improve heartbeat performance for users with memory plugins enabled.

Problem to solve

Current Behavior

Supermemory currently runs on every agent turn without distinguishing between:

  • Heartbeat runs - Periodic background checks (every 15-30m)
  • User interactions - Actual conversations with the user
// Current implementation (index.ts:46-59)
if (cfg.autoRecall) {
    const recallHandler = buildRecallHandler(client, cfg)
    api.on("before_agent_start", (event, ctx) => {
        // Runs on ALL turns including heartbeats
        return recallHandler(event, ctx)
    })
}

if (cfg.autoCapture) {
    api.on("agent_end", buildCaptureHandler(client, cfg, getSessionKey))
    // Runs on ALL turns including heartbeats  
}

The Problem

  • Heartbeats are designed to be lightweight "check-ins"
  • Memory recall adds latency to heartbeat responses
  • Memory capture stores HEARTBEAT_OK responses that have little long-term value
  • Each heartbeat wastes tokens on unnecessary memory API calls

OpenClaw SDK Solution

The SDK will expose isHeartbeat in the hook context (see openclaw/openclaw#24585) in:

  • Type: src/plugins/types.ts ~line 320
  • Runtime: src/auto-reply/reply/agent-runner.ts ~line 250-260
// SDK will provide this in src/plugins/types.ts:
export type PluginHookAgentContext = {
    agentId?: string;
    sessionKey?: string;
    sessionId?: string;
    workspaceDir?: string;
    messageProvider?: string;
    isHeartbeat?: boolean;  // ✅ NEW - Available after SDK update
};

Proposed Solution

Add new optional configuration fields to SupermemoryConfig:

export type SupermemoryConfig = {
    // ... existing fields ...
    
    /**
     * Skip memory recall during heartbeat runs.
     * Default: true (skip to save costs)
     */
    skipRecallOnHeartbeat?: boolean
    
    /**
     * Skip memory capture during heartbeat runs.
     * Default: true (skip to save costs)
     */
    skipCaptureOnHeartbeat?: boolean
}

JSON Schema Update

{
  "properties": {
    "skipRecallOnHeartbeat": {
      "type": "boolean",
      "default": true,
      "description": "Skip memory recall during heartbeat runs to reduce costs"
    },
    "skipCaptureOnHeartbeat": {
      "type": "boolean", 
      "default": true,
      "description": "Skip memory capture during heartbeat runs to reduce costs"
    }
  }
}

Implementation Steps

1. Update config.ts
Add new config fields with defaults:

return {
    // ... existing fields ...
    skipRecallOnHeartbeat: (cfg.skipRecallOnHeartbeat as boolean) ?? true,
    skipCaptureOnHeartbeat: (cfg.skipCaptureOnHeartbeat as boolean) ?? true,
}

2. Update index.ts (lines 46-59)
Check isHeartbeat flag in hooks. Note: Need to cast ctx to access the new field until SDK types are updated:

if (cfg.autoRecall) {
    const recallHandler = buildRecallHandler(client, cfg)
    api.on("before_agent_start", (event, ctx) => {
        // Cast ctx to access isHeartbeat once SDK provides it
        const agentCtx = ctx as PluginHookAgentContext & { isHeartbeat?: boolean }
        if (cfg.skipRecallOnHeartbeat && agentCtx.isHeartbeat) {
            log.debug("skipping recall during heartbeat")
            return
        }
        if (ctx.sessionKey) sessionKey = ctx.sessionKey as string
        return recallHandler(event, ctx)
    })
}

if (cfg.autoCapture) {
    const captureHandler = buildCaptureHandler(client, cfg, getSessionKey)
    api.on("agent_end", (event, ctx) => {
        const agentCtx = ctx as PluginHookAgentContext & { isHeartbeat?: boolean }
        if (cfg.skipCaptureOnHeartbeat && agentCtx.isHeartbeat) {
            log.debug("skipping capture during heartbeat")
            return
        }
        return captureHandler(event, ctx)
    })
}

3. Update openclaw.plugin.json
Add UI hints for new options:

{
  "skipRecallOnHeartbeat": {
    "label": "Skip Recall on Heartbeat",
    "help": "Skip memory recall during heartbeat runs to reduce API costs",
    "advanced": true
  },
  "skipCaptureOnHeartbeat": {
    "label": "Skip Capture on Heartbeat", 
    "help": "Skip memory capture during heartbeat runs to reduce API costs",
    "advanced": true
  }
}

Alternatives Considered

  • Skip heartbeat handling entirely: Not viable - heartbeats are important for OpenClaw's autonomous operation
  • Detect heartbeat by prompt content: Fragile - relies on string matching the configured heartbeat prompt
  • Always skip on heartbeat: Too restrictive - some users may want memory during heartbeats for specific workflows

The proposed configuration approach gives users control while defaulting to cost-saving behavior.

Impact

Performance:

  • Faster heartbeat responses (no memory search latency)
  • Reduced CPU/memory usage during periodic checks

Cost:

  • Lower API calls to Supermemory service during heartbeats
  • Estimated savings: ~15-30% reduction in Supermemory API usage for heartbeat-heavy deployments

User Experience:

  • Backward compatible - defaults maintain current behavior if desired
  • Advanced option for cost-conscious users

Evidence/Examples

Configuration Examples

Default (Cost-Optimized):

{
  "plugins": {
    "entries": {
      "openclaw-supermemory": {
        "enabled": true,
        "config": {
          "apiKey": "${SUPERMEMORY_OPENCLAW_API_KEY}",
          "autoRecall": true,
          "autoCapture": true,
          "skipRecallOnHeartbeat": true,
          "skipCaptureOnHeartbeat": true
        }
      }
    }
  }
}

Always Active:

{
  "plugins": {
    "entries": {
      "openclaw-supermemory": {
        "enabled": true,
        "config": {
          "apiKey": "${SUPERMEMORY_OPENCLAW_API_KEY}",
          "autoRecall": true,
          "autoCapture": true,
          "skipRecallOnHeartbeat": false,
          "skipCaptureOnHeartbeat": false
        }
      }
    }
  }
}

Additional Information

User Configuration Examples

Default (Cost-Optimized)

{
  "plugins": {
    "entries": {
      "openclaw-supermemory": {
        "enabled": true,
        "config": {
          "apiKey": "${SUPERMEMORY_OPENCLAW_API_KEY}",
          "autoRecall": true,
          "autoCapture": true,
          "skipRecallOnHeartbeat": true,   // ✅ Skip to save costs
          "skipCaptureOnHeartbeat": true   // ✅ Skip to save costs
        }
      }
    }
  }
}

Always Active (If heartbeats need memory context)

{
  "plugins": {
    "entries": {
      "openclaw-supermemory": {
        "enabled": true,
        "config": {
          "apiKey": "${SUPERMEMORY_OPENCLAW_API_KEY}",
          "autoRecall": true,
          "autoCapture": true,
          "skipRecallOnHeartbeat": false,  // ❌ Always recall
          "skipCaptureOnHeartbeat": false  // ❌ Always capture
        }
      }
    }
  }
}

Benefits

  1. Cost Reduction: Skip unnecessary API calls during heartbeats
  2. Faster Heartbeats: No memory search latency added to heartbeat responses
  3. Cleaner Memory Store: Avoid storing HEARTBEAT_OK and periodic check-in noise
  4. Backward Compatible: Default to current behavior, opt-in to optimization

Risks / Considerations

Risk Mitigation
Breaking change Both new fields are optional with safe defaults
Users relying on heartbeat memory Documentation will explain the option
SDK not yet updated Implementation blocked until SDK issue resolved

Dependencies

  • OpenClaw SDK exposes isHeartbeat in PluginHookAgentContext
  • Plugin SDK types updated in @supermemory/openclaw-supermemory

Checklist

  • Update SupermemoryConfig type in config.ts
  • Update parseConfig() to handle new fields
  • Update index.ts to check ctx.isHeartbeat in hooks
  • Update openclaw.plugin.json JSON schema
  • Update openclaw.plugin.json UI hints
  • Update README.md with new options
  • Add tests for heartbeat skip logic

Labels: enhancement, performance, cost-optimization
Priority: medium (blocked on SDK change)
Estimated Effort: 2-3 hours once SDK unblocked

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions