Agent ID format
Every agent ID follows a strict prefix convention so the type is self-describing on the wire:
| Type | ID format | Example |
|---|---|---|
| Consumer | consumer-{user_uuid} | consumer-7a4b2c9f-... |
| Venue | venue-{venue_uuid} | venue-b9e1d3a8-... |
| Platform bridge | CLAWPULSE_PLATFORM_AGENT_ID env var | agnt-bridge |
Consumer agents
Every user gets one consumer agent. register_consumer_agent is called from the user registration flow as a background task. It builds the DNA, posts it to ClawPulse via cp.register_agent_profile, and writes users.cp_agent_id on success.
def _consumer_dna(user: User) -> dict:
return {
"capabilities": {
"streams": ["news", "weather", "social"],
"tools": ["venue_search", "create_booking", "set_reminder"],
"role": "consumer_concierge",
},
"preferences": {
"subjects": ["news.bali.>", "weather.bali.>", "action.>"],
"minImportance": 0.4,
},
"behavioral": {
"autoReconnect": True,
"cursorPersist": True,
"city": user.city or "Bali",
"language": user.language or "en",
},
}Venue agents
Venue agents are registered when a VenueClient completes onboarding. register_venue_agent takes both the venue_id and the venue_client_id, optionally enforces the expected owner, and triggers a sync of the per-venue inbox loop when registration succeeds.
def _venue_dna(*, venue_name: str, venue_category: str | None, venue_area: str | None) -> dict:
return {
"capabilities": {
"streams": ["watcher", "action"],
"tools": ["venue_create_booking", "venue_send_menu", "escalate_to_human"],
"role": "venue_concierge",
"venue_name": venue_name,
"venue_category": venue_category or "",
"venue_area": venue_area or "",
},
"preferences": {
"subjects": ["action.>", "watcher.>"],
"minImportance": 0.5,
},
"behavioral": {
"autoReconnect": True,
"inboxPollEnabled": True,
},
}Inbox loop sync
After a successful venue registration the function imports sync_venue_inbox_loops_now from app.core.background_runtimeand awaits it. This is what makes the new venue agent's inbox actually get polled in production.
Platform bridge agent
Exactly one platform agent exists per deployment. Its ID comes from the CLAWPULSE_PLATFORM_AGENT_ID env var. The ensure_platform_agent_registered function is called from the FastAPI lifespan hook so traffic never flows before the bridge is known to ClawPulse.
def _platform_dna() -> dict:
return {
"capabilities": {
"streams": ["booking", "status", "action"],
"tools": ["send_message", "drain_inbox", "ack_messages"],
"role": "booking_bridge",
},
"preferences": {
"subjects": ["booking.>", "status.a2a.>", "action.>"],
"minImportance": 0.5,
},
"behavioral": {
"autoReconnect": True,
"cursorPersist": True,
"idempotencyRequired": True,
},
}Reconciliation
Deploys can drift: a venue may be marked onboarded_at in the database but never actually have a cp_agent_id because the original registration failed. reconcile_onboarded_venue_agents is the safety net: it walks the table, re-registers any venue client with a NULL cp_agent_id, and patches the row on success.
The function returns a summary dict with found, registered, and failed counts. Call it manually after a ClawPulse outage, or schedule it as a recurring health job if drift becomes common.
Failure mode
Every function in this module is wrapped in try / except and logs via logger.exception on failure. If ClawPulse is not configured at all (CLAWPULSE_API_KEY empty), registration skips with a warning and returns False. Callers check the return value before treating the agent as registered.
Related
- ClawPulse gateway— the underlying client this module calls into
- A2A protocol— the envelope shape the registered agents send and receive