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>
119 lines
3.2 KiB
Markdown
119 lines
3.2 KiB
Markdown
# 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
|
|
|
|
```typescript
|
|
// 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
|