@ext.skeleton reference
@ext.skeleton reference — declare ambient live state and entity feeds for Webbee, covering section naming, return contracts, and TTL intervals.
@ext.skeleton registers a live data probe that the web-kernel queries on a per-user schedule. The structured snapshot the probe returns is stored in the user's isolated environment and injected into the routing context on every chat turn as ambient awareness.
The LLM does not call skeleton tools explicitly — it sees their output as ambient background facts.
Use skeletons for "what does the user have right now" state: unread counters, active projects, connected accounts, monitor alerts, and key entity summaries.
Not a UI primitive
@ext.skeleton is strictly a data probe consumed by the AI Agent. It has no rendering, no panel slot, and no React component. If you are building anything visible to the user — sidebars, dashboards, editors, settings forms — use @ext.panel instead. Inside a panel, fetch state via ctx.cache (short-lived) or ctx.store (persistent). ctx.skeleton.get() is restricted exclusively to @ext.skeleton handlers and raises SkeletonAccessForbidden from any other context.
Where it lives
@ext.skeleton is a method on the Extension instance (not on ChatExtension). Register it from the same file that holds your ext = Extension(...), or import ext from app.py into a dedicated skeleton.py module — the pattern used across production extensions.
from imperal_sdk import Extension
ext = Extension(
"projects",
display_name="Projects",
description="Manage active projects and entities.",
icon="icon.svg",
actions_explicit=True,
)
@ext.skeleton("projects", alert=True, ttl=300)
async def skeleton_refresh_projects(ctx) -> dict:
...Signature
def skeleton(
self,
section_name: str,
*,
alert: bool = False,
ttl: int = 300,
description: str = "",
) -> Callable:Parameters
section_name
str · positional
Required. Positional. The name for this skeleton section. The decorator registers your handler under the tool name skeleton_refresh_{section_name}.
- Must be a valid identifier: ASCII letters, numbers, underscores (e.g.
"status","inbox","projects_overview"). - Keep it concise — the section name is used in logs, audit records, and context framing.
- Each call to
@ext.skeletonmust use a uniquesection_namewithin an extension.
alert
bool · default: False
When alert=True, the platform monitors the returned snapshot between refresh cycles:
- After every skeleton refresh, the web-kernel diffs the previous snapshot against the new one.
- If the snapshots differ, the web-kernel calls
skeleton_alert_{section_name}(ctx, old=<prev>, new=<next>). - If that alert tool returns a non-empty string, Webbee proactively notifies the user about the change.
@ext.skeleton("tasks", alert=True, ttl=60)
async def skeleton_refresh_tasks(ctx) -> dict:
overdue = await ctx.store.count("tasks", where={"overdue": True})
return {"response": {"overdue_count": overdue}}
@ext.tool(
"skeleton_alert_tasks",
description="Alerts user when overdue count increases.",
)
async def skeleton_alert_tasks(
ctx,
old: dict | None = None,
new: dict | None = None,
) -> dict:
if not new:
return {"response": ""}
old_c = (old or {}).get("overdue_count", 0)
new_c = new.get("overdue_count", 0)
if new_c > old_c:
return {"response": f"You have {new_c - old_c} new overdue task(s)."}
return {"response": ""}ttl
int · default: 300
Refresh interval in seconds. Defines how frequently the platform re-queries this probe for each active user.
30–60s: High-velocity feeds (unread email counts, active outages, live sensor alerts).120–300s: Standard operational state (active projects, task boards, carts).300–600s: Stable configuration & catalogs (connected services, database schemas, plan limits).
description
str · default: ""
Human-readable description for Developer Portal manifests and operational logs. Defaults to "Skeleton refresh: {section_name}".
Return Contract
Every @ext.skeleton handler must return a dictionary with a single "response" key containing scalar fields and entity cards:
return {
"response": {
"scalar_metric": 42,
"is_synced": True,
"key_entities": [
{"id": "ent_1", "name": "Primary", "status": "active"},
{"id": "ent_2", "name": "Secondary", "status": "pending"},
],
}
}1. What to put into a Skeleton (Entity Cards & Ambient State)
- Top-level scalar metrics & counters:
unread_count,active_servers,overdue_tasks. - Connected accounts & targets:
[{email, provider, is_active}],[{host, ip, status}]. - Key Entity Cards (up to 50–200 KB per section):
- Identity:
idor{type}_id(e.g.customer_id,project_id,invoice_id). - Label:
name,title, orlabel. - State:
status,state, orstage(e.g.active,paid,down). - Key attributes:
tier,amount,role,tags.
- Identity:
2. What NOT to put into a Skeleton (Heavy Payloads)
| Content Type | Why it does not belong in Skeleton | Where it belongs |
|---|---|---|
| Full email / message bodies | Consumes unnecessary context; unneeded for routing | Dedicated @chat.function (mail.read_email) |
| Unbounded raw database tables | Skeletons are ambient summaries, not database replicas | Paginated list tools (tasks.list_tasks(limit=...)) |
| Raw binary data / Base64 files | Skeletons only carry structured JSON metadata | ctx.store or dedicated download endpoints |
| Full historical log streams | Historical analysis is done on-demand | Diagnostic query tools |
Reading Previous Snapshots: ctx.skeleton.get()
ctx.skeleton.get(section) reads the previously stored snapshot for a section. It is available only inside @ext.skeleton handlers (or alert companions):
@ext.skeleton("inbox", ttl=60)
async def skeleton_refresh_inbox(ctx) -> dict:
prev_snapshot = await ctx.skeleton.get("inbox")
# compare or compute diffs if needed
...Access Forbidden outside skeletons
Calling ctx.skeleton.get() from a standard @chat.function or @ext.panel handler raises SkeletonAccessForbidden at runtime (Validator V24-AST). Panels and tools must read user state via ctx.cache or ctx.store.
Cross-links
@ext.cache_model reference
@ext.cache_model reference — register typed cache entries with TTL for fast repeat reads, per-user scoping, key-safety rules, and 64 KB value-size caps.
@ext.schedule reference
@ext.schedule reference — cron expressions and options for periodic background jobs on the ICNLI AI Cloud OS: system context, fan-out pattern, idempotency.