package.json
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.
{
"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
| Key | Declares | Default |
|---|---|---|
agents | Agent YAML files. At least one is required. | agents/ |
skills | Skills. | skills/ |
prompts | Prompt templates. | prompts/ |
extensions | Tools and hooks. | None |
mcp | MCP servers the recipe may use. | None |
channels | Slack only (Put your agent in Slack). | None |
runtime | Python 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.yamlThe runtime installs production dependencies with pnpm. Without a committed pnpm-lock.yaml, check warns and every install resolves the ranges afresh.
| Rule | Detail |
|---|---|
| Lockfile | Only pnpm-lock.yaml. package-lock.json, npm-shrinkwrap.json, and yarn.lock are rejected. |
devDependencies | Never installed in a managed runtime. |
peerDependencies | Only 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:
{
"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):
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.jsonEvery 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.”