Skip to Content
Platform
Buildpackage.json

package.json

The manifest names the recipe, declares what it owns, and decides what ships.

package.json is the recipe manifest: npm fields describe the package, and the pi block tells Recipes where agents and capabilities live. The smallest recipe that runs is { "name": "my-agent", "pi": {} } plus agents/agent.yaml.

package.json
{ "name": "support-agent", "version": "0.1.0", "type": "module", "dependencies": { "exceljs": "4.4.0" }, "pi": { "agents": ["agents/*.yaml"], "skills": ["skills/**/SKILL.md"], "prompts": ["prompts/*.md"], "extensions": ["extensions/*.ts"], "mcp": { "servers": [] }, "runtime": {} } }

pi keys

KeyDeclaresDefault
agentsAgent YAML files. At least one is required.agents/
skillsSkills.skills/
promptsPrompt templates.prompts/
extensionsTools and hooks.None
mcpMCP servers the recipe may use.None
channelsSlack only (Put your agent in Slack).None
runtimePython and system requirements (below).None

An explicit [] resolves nothing. Every path or glob must match something and stay inside the package.

Add Node dependencies

pnpm add exceljs git add package.json pnpm-lock.yaml

The runtime installs production dependencies with pnpm. Without a committed pnpm-lock.yaml, check warns and every install resolves the ranges afresh.

RuleDetail
LockfileOnly pnpm-lock.yaml. package-lock.json, npm-shrinkwrap.json, and yarn.lock are rejected.
devDependenciesNever installed in a managed runtime.
peerDependenciesOnly packages the runtime provides, such as @earendil-works/pi-coding-agent, @introspection-ai/recipes, typebox.

Use Python

Keep a uv project inside the recipe with a committed uv.lock, and declare it:

package.json
{ "pi": { "runtime": { "python": { "project": "python", "lockfile": "python/uv.lock", "version": ">=3.12,<3.15", "imports": ["pandas"] } } } }

At boot the runtime runs uv sync --frozen --no-dev into <recipe>/.pi/python and puts it on PATH; imports must import before the agent starts. introspection local validates the declaration but does not build it: run uv sync yourself.

Require a system package

The one bundle today is document.pdf-tools, version 1 (pdftotext, pdftoppm):

{ "pi": { "runtime": { "system": { "packages": [{ "id": "document.pdf-tools", "version": "1" }] } } } }

An unknown id passes check and fails the task at boot.

Control what ships

A version is the files under the runtime manifest’s path at one commit, minus .gitignored files and .recipeignore matches (Gitignore syntax):

.recipeignore
README.md evals/ python/tests/

Never ignore agents, extensions, skills, lockfiles, or files/. .recipeignore does not hide secrets: keep them out of Git.

Give every task the same files

Put fixtures and reference documents in files/. A managed task reads them at /workspace/files/<name> (read-only); under introspection local they stay at files/<name> in the recipe directory. A managed task stops at startup on a symlink (which check and local accept) or a name that collides with a task upload. Sizes: Limits.

Several recipes in one repository

One runtime manifest per runtime in .introspection/ at the repository root, each path pointing at its recipe:

repo/ ├── .introspection/ │ ├── support-agent.yaml # path: agents/support │ └── billing-agent.yaml # path: agents/billing └── agents/ ├── support/package.json └── billing/package.json

Every push to the default branch builds a new version of every runtime in the repository; builds cannot be limited to changed paths (Deploy your agent).

Or ask your agent: “Add pandas as a locked Python project and keep evals/ out of the deployed recipe.”

Last updated on