Imperal Docs
Core Concepts

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

๐Ÿapp.py
๐Ÿ“‹imperal.json

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:

๐Ÿapp.py
๐Ÿ“‹imperal.json
๐Ÿhandlers_chat.py
๐Ÿhandlers_panel.py
๐Ÿhandlers_skeleton.py
๐Ÿstate.py
๐Ÿapi_client.py
๐ŸŒi18n.json
๐Ÿ“–README.md

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:

app.py
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:

ValidatorWhat
V14Manifest schema v3 conformance
V15Display name + description not empty/placeholder
V16Icon resolves
V17Pydantic params model is module-scope (not function-local)
V18Pydantic params model has no forward references
V19All @chat.function descriptions โ‰ฅ 10 chars and not boilerplate
V20action_type โˆˆ destructive
V21icon.svg declared + valid (XML-validated, viewBox, โ‰ค100KB, no embedded raster)
V22No print() in handlers (use ctx.log or structured returns)
V24Handlers use ctx.http, never ctx.skeleton.* (AST scan)
V31Extension(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

On this page