Skeleton — surface state and entities to Webbee
Surface live counts and compact entity cards to Webbee so the agent always sees current state and entities before every chat turn.
A skeleton is LLM-facing, not user-facing. Every time the user sends a chat message, the web-kernel injects ambient state and entity awareness into the intent classifier and routing context.
Because of this ambient layer:
- When the user asks "what tasks do I have today?", Webbee answers directly from the skeleton's live counts.
- When the user says "deploy the Mobile App project", Webbee immediately grounds "Mobile App" to
project_id="proj_10"from the ambient entity cards.
Complete recipe: Tasks & Projects Skeleton
Below is a complete, production-grade example from tasks/skeleton.py showing both scalar counters and compact entity summaries:
from typing import Any
from imperal_sdk import Extension
ext = Extension(
"tasks",
display_name="Tasks",
description="Task management with ambient entity tracking.",
icon="icon.svg",
actions_explicit=True,
)
# ── Skeleton: Surface pending counts + active project entity cards ───────────
# alert=True: The web-kernel calls skeleton_alert_tasks when overdue counts change.
@ext.skeleton(
"tasks_and_projects",
alert=True,
ttl=120,
description="Live overdue/today task counters and active project entity cards.",
)
async def skeleton_refresh_tasks(ctx) -> dict[str, Any]:
"""Refresh task counters and active projects. Pure read — idempotent."""
uid = ctx.user.imperal_id
if not uid:
return {"response": {"note": "anonymous user"}}
# 1. Fetch live metrics from storage / upstream service
overdue_count = await ctx.store.count("tasks", where={"is_overdue": True})
today_count = await ctx.store.count("tasks", where={"is_due_today": True})
total_active = await ctx.store.count("tasks", where={"is_completed": False})
# 2. Fetch key active project entities for ambient entity grounding
projects = await ctx.store.query(
"projects",
where={"status": "active"},
order_by="-updated_at",
limit=10,
)
# 3. Shape compact entity cards (id, name, status, task_count)
active_project_cards = [
{
"project_id": p.id,
"name": p.data.get("name", "Untitled"),
"task_count": p.data.get("pending_tasks", 0),
"status": p.data.get("status", "active"),
}
for p in projects.data
]
# 4. Return structured envelope
return {
"response": {
"overdue_count": overdue_count,
"today_count": today_count,
"total_active_tasks": total_active,
"active_projects": active_project_cards,
}
}
# ── Skeleton Alert: Proactively warn user when overdue tasks spike ────────────
@ext.tool(
"skeleton_alert_tasks",
description="Fires when task statistics change across refresh cycles.",
)
async def skeleton_alert_tasks(
ctx,
old: dict[str, Any] | None = None,
new: dict[str, Any] | None = None,
) -> dict[str, str]:
"""Notify the user proactively if overdue tasks increased."""
if not new:
return {"response": ""}
overdue = new.get("overdue_count", 0)
old_overdue = (old or {}).get("overdue_count", 0)
today = new.get("today_count", 0)
old_today = (old or {}).get("today_count", 0)
alerts: list[str] = []
if overdue > 0 and overdue > old_overdue:
delta = overdue - old_overdue
alerts.append(f"{delta} new overdue task(s) (now {overdue} total)")
if today > 0 and today != old_today:
alerts.append(f"{today} task(s) due today")
return {"response": " · ".join(alerts)}Best Practices Breakdown
- Compact Entity Cards: Notice how
active_projectsincludes onlyproject_id,name,task_count, andstatus. This is enough for Webbee to resolve "how is the Mobile App project doing?" toproject_id="proj_10". - Key Entities Selection: Surface active, high-priority, or recently modified entities. If there are 100 projects, surface the active ones; the agent will use
list_projects()if the user asks for the full historical catalog. - No Heavy Payloads: Never return full descriptions, markdown content, or nested task lists in the skeleton. Keep it tight!
- Resilient Fallbacks: Check
resp.okand handle missing user context gracefully so background refreshes never throw unhandled exceptions.
Cross-links
Webhook handler — Stripe-style HMAC verification
Verify inbound webhooks in a Webbee extension Stripe-style: HMAC-SHA256 with a constant-time compare, plus replay protection and an idempotency guard.
Cached inbox — typed get_or_fetch with TTL
Cache an inbox in your Webbee extension with a typed get_or_fetch and a short TTL, so expensive list fetches are reused instead of re-run on every read.