Extensions
What an extension actually is, its file shape, lifecycle, and the four kinds of capabilities it can declare
An extension is a Python package that gives Webbee new things it can do for the user. This page explains what's actually inside one and what each piece is for.
The mental model
Identity
Every extension has a unique name (e.g. tasks-mini), a display name, a description, and an icon. This is what users see in the marketplace.
Capabilities
Four types: chat functions, panels, skeletons, and background jobs. An extension can have any combination.
Manifest
A JSON file (imperal.json) that declares everything machine-readably. Auto-generated from your code.
Handlers
Plain Python functions, decorated. The SDK wires them to the [web-kernel](/en/reference/glossary/) at install time.
The minimal extension
That's the floor. app.py declares your Extension and at least one capability. imperal.json is auto-generated from app.py by imperal build.
A typical real-world extension grows into:
Plain Python โ no framework structure imposed. Whatever module split makes sense is fine.
The four capability types
@chat.function
The big one. A tool the LLM can call when users speak. Typed parameters, structured return, automatic audit.
@ext.panel
A UI block rendered in the Imperal Panel. `slot=` selects left / right / center; the handler returns a `ui.*` UINode tree.
@ext.skeleton
A live data probe. Web-kernel queries it on a schedule and feeds the result into Webbee's awareness.
@ext.schedule
A background job โ cron-like. Runs on a [ICNLI Worker](/en/reference/glossary/) on a schedule. Useful for backfills, sync jobs, periodic cleanup.
You'll learn each one in depth in its own concept page. For now: an extension can mix and match. A mail extension typically has all four.
The Extension class
The first thing in app.py is always:
from imperal_sdk import Extension
ext = Extension(
display_name="Tasks (Mini)", # human-readable, shown in marketplace
description="A tiny task manager", # short โ one paragraph max
icon="check-square", # Lucide icon name OR url to PNG
actions_explicit=True, # federal default โ see below
)Prop
Type
actions_explicit=True is the federal default
When True, Webbee won't fire your tools unless the intent classifier is confident enough that the user explicitly asked. This is the right default for anything that touches user data. Set False only if your extension is purely informational (e.g. a glossary lookup).
The lifecycle
Develop locally
You write app.py, run imperal build, smoke-test with imperal_sdk.cli test --tool .... No web-kernel needed.
Validate
imperal validate runs all 11 federal validators (V14-V22 + V24 + V31). Every one is ERROR-severity โ failing any blocks publishing.
The validators check:
| Validator | What |
|---|---|
| V14 | Manifest schema v3 conformance |
| V15 | Display name + description not empty/placeholder |
| V16 | Icon resolves |
| V17 | Pydantic params model is module-scope (not function-local) |
| V18 | Pydantic params model has no forward references |
| V19 | All @chat.function descriptions โฅ 10 chars and not boilerplate |
| V20 | action_type โ destructive |
| V21 | icon.svg declared + valid (XML-validated, viewBox, โค100KB, no embedded raster) |
| V22 | No print() in handlers (use ctx.log or structured returns) |
| V24 | Handlers use ctx.http, never ctx.skeleton.* (AST scan) |
| V31 | Extension(system=True) reserved for first-party Imperal authors |
Publish to the Developer Portal
Upload your packaged extension at panel.imperal.io/developer (drag-and-drop). The portal re-runs every validator server-side, then a reviewer (or auto-review for trusted publishers) checks your manifest matches your code and approves.
Install
End users install your extension from the marketplace. Behind the scenes:
- Registry stores
(user_id, ext_id, version, scopes_granted, status='installed') - The next chat turn for that user includes your tools in the intent classifier's tool list
- Skeletons start firing on schedule
- Panel slots become available
Run
Your handlers run on the platform's worker fleet on demand. You don't manage processes, deployments, or scaling.
Update
Bump the version in your manifest, publish again. Existing installs migrate on the next turn (no downtime). The Developer Portal lets you stage a release to a percentage of users for canary.
The federal-blocking rule
Don't edit shipped extensions in the deployed-extension directory directly
Imperal-Cloud-deployed extensions live in the deployed-extension directory. They have inner-git managed by the Developer Portal โ direct edits cause replay drift. Always work from your local source and re-publish.
What an extension isn't
Not a web service
No HTTP server. No port to expose. The web-kernel calls your handlers in-process via [ICNLI](/en/reference/glossary/) Worker.
Not a daemon
No long-running event loop in your code. The web-kernel triggers handlers; they return; that's the loop.
Not a UI framework
Panels return JSON layouts. The Imperal Panel React app does the rendering. You don't ship React/HTML.
Not a job queue
Use @ext.schedule โ ICNLI Worker handles cron, retries, timeouts. Don't roll your own.
Where to go next
@chat.function in depth
Parameters, return shapes, action types, chain hints, [Pydantic feedback loop](/en/reference/glossary/).
Skeletons
How Webbee always knows your state without prompting.
Panels
UI surfaces โ sidebars, editors, settings panes, dashboards.
Web-kernel context (ctx)
The single object every handler gets. user, tenant, api, secrets, history.