Extensions
Tools you write in TypeScript, owned by the package.
When a job needs a tool Pi does not have, write it. An extension is a TypeScript file the package declares and every session in it loads.
Pi loads the TypeScript directly, so there is nothing to compile and no bundler to configure. It also resolves @earendil-works/pi-coding-agent, typebox, @earendil-works/pi-ai and @earendil-works/pi-tui for you, so those are not dependencies you declare. Anything else your extension imports goes in dependencies, with an npm install in the package.
Declared once, gated per agent
Extensions load for every agent in the package, including delegated children. Registering a tool does not expose it: an agent still has to list check_citation in its tools. Two roles can therefore share one extension and see different halves of it.
Knowing which recipe you are in
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. For the resolved agent itself, @introspection-ai/recipes/extensions exposes the session context.
A host can add its own
A host passes extensionFactories and customTools to add platform tools alongside the package's own, for things a recipe should not carry: file uploads, workspace persistence, its own telemetry. Those are host resources, not portable ones, so they stay out of the package. See the SDK reference.