BE-CAL-003: Add Agent model with status/heartbeat/exhausted fields

- New app/models/agent.py with Agent model, AgentStatus & ExhaustReason enums
- Agent has 1-to-1 FK to User, unique agent_id (OpenClaw $AGENT_ID),
  claw_identifier (OpenClaw instance, convention-matches MonitoredServer.identifier)
- Status fields: status (idle/on_call/busy/exhausted/offline), last_heartbeat
- Exhausted tracking: exhausted_at, recovery_at, exhaust_reason (rate_limit/billing)
- User model: added 'agent' back-reference (uselist=False)
- Schemas: AgentResponse, AgentStatusUpdate, UserCreate now accepts agent_id+claw_identifier
- UserResponse: includes agent_id when agent is bound
- Users router: create_user creates Agent record when agent_id+claw_identifier provided
- Auto-migration: CREATE TABLE agents in _migrate_schema()
- Startup imports: agent and calendar models registered
This commit is contained in:
zhi
2026-03-30 20:47:44 +00:00
parent a9b4fa14b4
commit 1c062ff4f1
5 changed files with 257 additions and 5 deletions

View File

@@ -176,6 +176,9 @@ class UserBase(BaseModel):
class UserCreate(UserBase):
password: Optional[str] = None
role_id: Optional[int] = None
# Agent binding (both must be provided or both omitted)
agent_id: Optional[str] = None
claw_identifier: Optional[str] = None
class UserUpdate(BaseModel):
@@ -192,6 +195,7 @@ class UserResponse(UserBase):
is_admin: bool
role_id: Optional[int] = None
role_name: Optional[str] = None
agent_id: Optional[str] = None
created_at: datetime
class Config:
@@ -389,6 +393,47 @@ class ProposalAcceptResponse(ProposalResponse):
from_attributes = True
# ---------------------------------------------------------------------------
# Agent schemas (BE-CAL-003)
# ---------------------------------------------------------------------------
class AgentStatusEnum(str, Enum):
IDLE = "idle"
ON_CALL = "on_call"
BUSY = "busy"
EXHAUSTED = "exhausted"
OFFLINE = "offline"
class ExhaustReasonEnum(str, Enum):
RATE_LIMIT = "rate_limit"
BILLING = "billing"
class AgentResponse(BaseModel):
"""Read-only representation of an Agent."""
id: int
user_id: int
agent_id: str
claw_identifier: str
status: AgentStatusEnum
last_heartbeat: Optional[datetime] = None
exhausted_at: Optional[datetime] = None
recovery_at: Optional[datetime] = None
exhaust_reason: Optional[ExhaustReasonEnum] = None
created_at: datetime
class Config:
from_attributes = True
class AgentStatusUpdate(BaseModel):
"""Payload for updating an agent's runtime status."""
status: AgentStatusEnum
exhaust_reason: Optional[ExhaustReasonEnum] = None
recovery_at: Optional[datetime] = None
# Backward-compatible aliases
ProposeStatusEnum = ProposalStatusEnum
ProposeBase = ProposalBase