Scheduled Execution: Heartbeats, Cronjobs, and Subagents #35
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Competitor
Kind/Documentation
Kind/Enhancement
Kind/Epic
Kind/Feature
Kind/Security
Kind/Story
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Scope/Core
Scope/Cross-Plugin
Scope/Plugin-System
Scope/Single-Plugin
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ultanio/cobot#35
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Cobot needs mechanisms for scheduled and delegated execution beyond the reactive message loop. This issue describes three related but distinct patterns.
1. Heartbeat
What: Periodic wake-up of the main agent session with full context.
Key properties:
heartbeat.md(or config) for instructionsHEARTBEAT_OKif nothing needs attentionOpenClaw equivalent: Gateway cron injects a system event into the main session.
Config example:
2. Cronjobs
What: Scheduled tasks that run in isolation without prior context.
Key properties:
Config example:
3. Subagents
What: On-demand spawned sessions for delegated work.
Key properties:
API example:
Tool example (for agent use):
Relationships
Open Questions
Heartbeat injection: How does heartbeat inject into the main session? Via communication plugin? Direct agent call?
Subagent lifecycle: How do subagents report back? Callback? Shared queue? Event?
Resource limits: How to prevent runaway subagents/cronjobs? Timeouts, token limits?
Plugin architecture:
heartbeat,cron,subagent)LLM sharing: Do subagents/cronjobs use the same LLM provider as main? Configurable?
Concurrency: Can multiple subagents run in parallel? Limit?
Related
Requesting architecture review to propose plugin structure.
Architecture Proposal: Scheduled Execution
After reviewing Cobot's plugin architecture and this issue, here's a detailed implementation proposal.
Design Philosophy
Following Cobot's principles: minimal, composable, self-sovereign. Each concept is a separate plugin, not a monolithic scheduler. Plugins can work independently or together.
Plugin Structure
Phase 1: Heartbeat Plugin (MVP)
Goal: Periodic wake-up of main agent with full context.
Approach
Heartbeat works within the main session by injecting a synthetic message into the communication flow. This preserves full context (history, memory, SOUL.md) since the agent's
respond()method handles it like any other message.Plugin Design
Key insight: By implementing
session.receive, heartbeat can inject messages into the normal flow. The session plugin aggregates allsession.receiveimplementations.Injection Method
Config Schema
Hook: Suppress Persistence
Heartbeat responses shouldn't clutter conversation history. Use a hook:
Persistence plugin checks
ctx["skip_persistence"]before saving.Example heartbeat.md
Phase 2: Cron Plugin
Goal: Scheduled tasks in isolated sessions (no shared context).
Approach
Cronjobs need a separate mini-agent that runs in isolation. This is different from heartbeat—no history injection, no memory context.
Plugin Design
Job Runner (Isolated)
Config Schema
Output Routing
Cron Expression Parsing
Use the
croniterlibrary (add to dependencies):Phase 3: Subagent Plugin
Goal: On-demand spawned sessions for delegated work.
Approach
Subagents are like cronjobs but triggered programmatically instead of scheduled. They run in isolation with explicit context passed to them.
Plugin Design
API Method
Tool Definition (for main agent)
The main agent can spawn subagents via tool:
Implementation
Concurrency Limits
Integration with agent.py
Minimal changes needed to
agent.py:1. Agent Reference for Plugins
Add method to set agent reference (like
set_registry):2. Hook: Skip Persistence
In persistence plugin:
New Interface: ScheduledTask
For consistency, define a shared interface:
Implementation Phases
Phase 1: Heartbeat (MVP) - ~2-3 days
heartbeat/plugin.pysession.receiveextensionskip_persistenceflagConfig additions:
Phase 2: Cronjobs - ~3-4 days
croniterdependencycron/plugin.pyConfig additions:
Phase 3: Subagents - ~2-3 days
subagent/plugin.pyConfig additions:
Tradeoffs & Concerns
1. Resource Usage
2. Heartbeat Injection
session.receiveis elegant but requires the communication plugin to aggregate properlyagent.respond()but requires agent reference3. Cronjob Isolation
4. Subagent Tool Recursion
5. Error Handling
on_errorhook, add retry logicOpen Questions for Discussion
Heartbeat response routing: Should heartbeat responses go anywhere? Options:
Cron job tools: Should cronjobs have access to tools? Risk vs utility?
Subagent result delivery: Sync (await) vs async (callback/filedrop)?
Summary
heartbeatsession.receivecronsubagentThis approach is composable (use any subset), minimal (each plugin is small), and extensible (extension points for customization).
Ready to start on Phase 1?
Several questions:
Also heartbeat is mentioned in the agent.py. This is a nogo! No references of plugins in other code or plugins which is not extending that plugin!
Valid concerns! Let me address:
1. Implementation Order
You're right — if cronjobs need isolated sessions (subagents), then subagents should come before cron, not after.
Revised order:
2. Heartbeat as Special Cron
Heartbeat could be implemented as:
So
cronplugin handles both:mode: isolated→ spawn subagent (no context)mode: main_session→ inject into main session (full context = heartbeat)3. No Plugin References in agent.py ❌
Agreed — this violates plugin architecture. Heartbeat/cron should not be referenced in agent.py.
How to inject into main session without touching agent.py:
Option A: Communication plugin extension
Option B: New extension point
scheduler.on_tickextension point in registryOption A keeps everything as "messages" which is cleaner.
Revised architecture:
No changes to agent.py needed. Cron uses existing communication aggregation for main_session mode, and subagent API for isolated mode.
Does this address the concerns?
📋 Revised Architecture Proposal (v2)
Based on feedback, here's the corrected architecture.
Implementation Order
subagentcronsubagentheartbeatcronPhase 1: Subagent Plugin
Purpose: Spawn isolated sessions for delegated work.
Interface:
Config:
Tool (for agent use):
Phase 2: Cron Plugin
Purpose: Schedule jobs — either isolated (via subagent) or in main session.
Config:
Modes:
isolated— Spawns subagent via SubagentProvider (no context)main_session— Injects via communication plugin (full context)Main session injection (no agent.py changes):
Phase 3: Heartbeat Plugin
Purpose: Convenience wrapper for the common "periodic main-session wake-up" pattern.
Config (simple):
Implementation:
HEARTBEAT.md convention:
Key Principles
comm.register_source()Dependency Graph
Open Questions
Communication source registration — Does the communication plugin support
register_source()or do we need to add it?Subagent LLM — Same provider as main, or configurable per-job?
Quiet hours — Skip entirely, or queue for later?
Heartbeat response handling — Log? Discard? Send somewhere?
I think the heartbeat should be enabled by default.
The cronjob plugin should have some logging: Whether it's activated at all and which cronjobs does it have configured.