Files
HarborForge.OpenclawPlugin/REFACTOR_PLAN.md
operator ec09578de3 feat: schedule cache, workflow-aligned prompts, dispatchInbound wakeup
1. ScheduleCache: local cache of today's schedule, synced every 5 min
   from HF backend via new getDaySchedule() API

2. Wakeup prompts updated to reference daily-routine skill workflows
   (task-handson, plan-schedule, slot-complete)

3. Agent wakeup via dispatchInboundMessageWithDispatcher (in-process)
   - Same mechanism as Discord plugin
   - Creates unique session per slot: agent:{agentId}:hf-calendar:slot-{slotId}
   - No WebSocket, CLI, or cron dependency
   - Verified working on test environment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 09:32:36 +00:00

3.2 KiB

CalendarScheduler Refactor Plan (v2)

Updated 2026-04-19 based on architecture discussion with hang

Current Issues

  1. process.env.AGENT_ID doesn't exist in plugins subprocess — always 'unknown'
  2. Heartbeat is per-agent but should be per-claw-instance (global)
  3. Scheduler only handles one agent — should manage all agents on this instance
  4. wakeAgent used api.spawn (non-existent) → now uses dispatchInboundMessage (verified)

Target Design

Plugin State

// Local schedule cache: { agentId → [slots] }
const schedules: Map<string, CalendarSlotResponse[]> = new Map();

Sync Flow (every 5 min)

1. GET /calendar/sync?claw_identifier=xxx
   - First call: server returns full { agentId → [slots] }
   - Subsequent: server returns diff since last sync
2. Update local schedules map
3. Scan schedules for due slots:
   for each agentId in schedules:
     if has slot where scheduled_at <= now && status == not_started:
       getAgentStatus(agentId, clawIdentifier) → busy?
       if not busy → wakeAgent(agentId)

Heartbeat (every 60s)

Simplified to liveness ping only:

POST /monitor/server/heartbeat
  claw_identifier: xxx
  → server returns empty/ack

No slot data in heartbeat response.

Wake Flow

dispatchInboundMessage:
  SessionKey: agent:{agentId}:hf-wakeup
  Body: "You have due slots. Follow the hf-wakeup workflow of skill hf-hangman-lab to proceed. Only reply WAKEUP_OK in this session."

Agent reads workflow → calls hf tools → sets own status to busy

Agent ID Resolution

  • Sync: agentId comes from server response (dict keys)
  • Wake: agentId from local schedules dict key
  • Tool calls by agent: agentId from tool ctx (same as padded-cell)

Backend API Changes Needed

New: GET /calendar/sync

GET /calendar/sync?claw_identifier=xxx
Headers: X-Claw-Identifier

Response (first call):
{
  "full": true,
  "schedules": {
    "developer": [slot1, slot2, ...],
    "operator": [slot3, ...]
  },
  "syncToken": "abc123"
}

Response (subsequent, with ?since=abc123):
{
  "full": false,
  "diff": [
    { "op": "add", "agent": "developer", "slot": {...} },
    { "op": "update", "agent": "developer", "slotId": 5, "patch": {...} },
    { "op": "remove", "agent": "operator", "slotId": 3 }
  ],
  "syncToken": "def456"
}

Existing: POST /calendar/agent/status

Keep as-is but ensure it accepts agentId + clawIdentifier as params:

POST /calendar/agent/status
  { agent_id, claw_identifier, status }

Implementation Order

  1. Backend: Add /calendar/sync endpoint
  2. Plugin: Replace CalendarBridgeClient single-agent design with multi-agent
  3. Plugin: Replace CalendarScheduler with new sync+check loop
  4. Plugin: wakeAgent uses dispatchInboundMessage (done)
  5. Plugin: Tool handlers get agentId from ctx (like padded-cell)

Files to Change

Backend (HarborForge.Backend)

  • New route: /calendar/sync
  • New service: schedule diff tracking per claw_identifier

Plugin

  • plugin/calendar/calendar-bridge.ts — remove agentId binding, add sync()
  • plugin/calendar/scheduler.ts — rewrite to multi-agent sync+check
  • plugin/calendar/schedule-cache.ts — already exists, adapt to multi-agent
  • plugin/index.ts — update wakeAgent, getAgentStatus to accept agentId