Skip to Content
Platform

Extensions

Extensions add recipe-owned code when instructions alone are not enough.

An extension is optional TypeScript code loaded by Pi. It can register a purpose-built tool, connect to Pi lifecycle events, or provide deterministic behavior that should not depend on model judgment.

The recipe manifest declares its extensions, and every agent session in the package loads them — including delegated children. Registering a tool does not expose it: an agent still has to list the tool in its tools allowlist, so two roles can share one extension and see different halves of it.

Pi loads the TypeScript directly — nothing to compile, no bundler. Recipe-owned tools receive the selected path and agent as PI_RECIPE_DIR and PI_AGENT_NAME, so an extension can find files in its own package without a hardcoded path.

Use an extension when the recipe needs a stable operation such as parsing a domain file, enforcing a structured transformation, or wrapping an approved internal library. Use a skill when the work is primarily a procedure the model should reason through, and use MCP for tools provided by an external service.

Extensions run as code in the Pi host, so keep them small, reviewable, and limited to the capability the recipe actually needs. Declare runtime dependencies in the recipe’s package.json and commit a lockfile for recipes distributed from Git.

See the standard’s extension documentation  for supported loading and dependency behavior.

Ask the user for input

An extension can pause for a question or approval with the portable interaction helpers from @introspection-ai/recipes/interactions. They work in local Pi, remote UIs, and headless hosts; the host decides how to render or resume the request. Return the helper result directly and keep interaction tools sequential so a pause cannot race another tool call.

See the interaction contract  for the askUserQuestion() and askUserApproval() examples. On the Introspection platform, these pauses surface as task interrupts: the task moves to awaiting_user and the caller resumes it — see Tasks and runs.

Last updated on