Files
HarborForge.OpenclawPlugin/docs/PLG-CAL-001-calendar-heartbeat-format.md
zhi 55d7d11a52 feat(plugin): PLG-CAL-001 - define Calendar heartbeat request/response format
- Add plugin/calendar/types.ts: TypeScript interfaces for heartbeat
  request/response (CalendarHeartbeatRequest/Response, CalendarSlotResponse,
  SlotAgentUpdate, all enums: SlotType, SlotStatus, EventType)
- Add plugin/calendar/calendar-bridge.ts: CalendarBridgeClient HTTP client
  with heartbeat(), updateSlot(), updateVirtualSlot(), reportAgentStatus()
- Add plugin/calendar/index.ts: module entry point exporting all public types
- Add docs/PLG-CAL-001-calendar-heartbeat-format.md: full specification
  documenting claw_identifier and agent_id determination, request/response
  shapes, error handling, and endpoint summary
- Update plugin/openclaw.plugin.json: add calendarEnabled,
  calendarHeartbeatIntervalSec, calendarApiKey config options; clarify
  identifier description as claw_identifier

Refs: HarborForge.NEXT_WAVE_DEV_DIRECTION.md §6, BE-AGT-001
2026-04-01 07:51:39 +00:00

7.1 KiB

PLG-CAL-001 — Calendar Heartbeat Format Specification

Task: HarborForge OpenclawPlugin / Monitor 联动
Subtask: PLG-CAL-001 — 插件侧定义 Calendar 心跳请求格式
Status: Implemented (types + client + spec)
Date: 2026-04-01


Overview

This document specifies the request/response format for the Calendar heartbeat communication between the OpenClaw HarborForge plugin and the HarborForge backend.

Heartbeat direction: Plugin → Backend

The plugin sends a heartbeat every minute (aligned with the existing Monitor heartbeat interval). The backend returns today's pending TimeSlots for the agent.


1. How claw_identifier is Determined

claw_identifier identifies the server/claw instance. It is the same value used in the Monitor heartbeat system (MonitoredServer.identifier).

Priority order:

  1. config.identifier — if set in the plugin config (harbor-forge.identifier)
  2. os.hostname() — auto-detected from the machine hostname (fallback)
// In plugin/calendar/calendar-bridge.ts
const clawIdentifier = baseConfig.identifier || hostname();

2. How agent_id is Determined

agent_id is the OpenClaw agent identifier ($AGENT_ID).

  • Set by OpenClaw at agent startup as an environment variable
  • The plugin reads it via process.env.AGENT_ID
  • Globally unique within a single OpenClaw gateway deployment
// In plugin/index.ts (caller)
const agentId = process.env.AGENT_ID || 'unknown';

3. Heartbeat Request

Endpoint: GET /calendar/agent/heartbeat

Headers

Header Value Notes
Content-Type application/json Always set
X-Agent-ID $AGENT_ID OpenClaw agent identifier
X-Claw-Identifier claw_identifier Server identifier

Request Body (JSON)

{
  "claw_identifier": "srv1390517",
  "agent_id": "developer"
}

Field Definitions

Field Type Source Notes
claw_identifier string Plugin config or hostname() Identifies the OpenClaw server instance
agent_id string process.env.AGENT_ID Identifies the agent session

4. Heartbeat Response

Success (HTTP 200)

{
  "slots": [
    {
      "id": 42,
      "virtual_id": null,
      "user_id": 1,
      "date": "2026-04-01",
      "slot_type": "work",
      "estimated_duration": 30,
      "scheduled_at": "09:00:00",
      "started_at": null,
      "attended": false,
      "actual_duration": null,
      "event_type": "job",
      "event_data": {
        "type": "Task",
        "code": "TASK-123"
      },
      "priority": 50,
      "status": "not_started",
      "plan_id": null
    }
  ],
  "agent_status": "idle",
  "message": "2 slots pending"
}

Field Definitions

Field Type Notes
slots CalendarSlotResponse[] Pending slots, sorted by priority DESC
agent_status AgentStatusValue Current backend-observed agent status
message string (optional) Human-readable summary

CalendarSlotResponse Fields

Field Type Notes
id number | null Real slot DB id. null for virtual slots.
virtual_id string | null plan-{plan_id}-{date}. null for real slots.
user_id number Owner HarborForge user id
date string ISO date YYYY-MM-DD
slot_type SlotType work | on_call | entertainment | system
estimated_duration number Minutes (1-50)
scheduled_at string ISO time HH:MM:SS
started_at string | null Actual start time when slot begins
attended boolean true once agent begins the slot
actual_duration number | null Real minutes when slot finishes
event_type EventType | null job | entertainment | system_event
event_data object | null See §4a below
priority number 0-99, higher = more urgent
status SlotStatus not_started | deferred | ...
plan_id number | null Source plan if materialized from SchedulePlan

§4a — event_data Shapes

When event_type == "job":

{
  "type": "Task",
  "code": "TASK-42",
  "working_sessions": ["session-id-1"]
}

When event_type == "system_event":

{
  "event": "ScheduleToday"
}

Valid events: ScheduleToday | SummaryToday | ScheduledGatewayRestart


5. Slot Update Requests (Plugin → Backend after execution)

After attending / finishing / deferring a slot, the plugin calls:

Real slot: PATCH /calendar/slots/{slot_id}/agent-update Virtual slot: PATCH /calendar/slots/virtual/{virtual_id}/agent-update

Headers

Same as heartbeat (see §3).

Request Body

{
  "status": "ongoing",
  "started_at": "09:02:31",
  "actual_duration": null
}
Field Type Required Notes
status SlotStatus Required New status after agent action
started_at string On attending ISO time HH:MM:SS
actual_duration number On finishing Real minutes

Status Transition Values

Action status value
Agent begins slot ongoing
Agent finishes slot finished
Agent defers slot deferred
Agent aborts slot aborted
Agent pauses slot paused

6. Backend Endpoint Summary

Method Path Auth Description
GET /calendar/agent/heartbeat X-Agent-ID + X-Claw-Identifier Fetch pending slots for today
PATCH /calendar/slots/{id}/agent-update X-Agent-ID + X-Claw-Identifier Update real slot status
PATCH /calendar/slots/virtual/{vid}/agent-update X-Agent-ID + X-Claw-Identifier Update virtual slot status
POST /calendar/agent/status X-Agent-ID + X-Claw-Identifier Report agent status change

7. Error Handling

  • Backend unreachable: Plugin logs warning, returns null from heartbeat.
    Agent continues to operate without Calendar integration.
  • Invalid credentials (401/403): Logged as error. No retry on same interval.
  • Rate limiting (429): Plugin should mark agent as Exhausted and not retry until the Retry-After header indicates.

8. TypeScript Reference

Full type definitions are in plugin/calendar/types.ts:

// Request
interface CalendarHeartbeatRequest {
  claw_identifier: string;
  agent_id: string;
}

// Response
interface CalendarHeartbeatResponse {
  slots: CalendarSlotResponse[];
  agent_status: AgentStatusValue;
  message?: string;
}

// Slot update
interface SlotAgentUpdate {
  status: SlotStatus;
  started_at?: string;   // ISO time HH:MM:SS
  actual_duration?: number;
}

9. Implementation Files

File Purpose
plugin/calendar/types.ts TypeScript interfaces for all request/response shapes
plugin/calendar/calendar-bridge.ts CalendarBridgeClient HTTP client
plugin/calendar/index.ts Module entry point
docs/PLG-CAL-001-calendar-heartbeat-format.md This specification