pi-pinata
An extensible subagent runtime for Pi with durable assignments, shared workspaces, and live conversation controls.
Package details
Install pi-pinata from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-pinata- Package
pi-pinata- Version
0.0.2- Published
- Nov 12, 2026
- Downloads
- 2,329/mo · 970/wk
- Author
- funsaized
- License
- MIT
- Types
- extension, skill, prompt
- Size
- 2.3 MB
- Dependencies
- 2 dependencies · 3 peers
Pi manifest JSON
{
"skills": [
"./skills/subagents"
],
"themes": [],
"prompts": [
"./prompts/scout.md",
"./prompts/builder.md",
"./prompts/reviewer.md",
"./prompts/pinata-review.md",
"./prompts/pinata-fix.md"
],
"extensions": [
"./engine/pi/extension.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
Piñata
Subagents for Pi, with conversations you can steer and work you can recover.
Piñata turns one Pi conversation into a working team. Send a scout to trace an unfamiliar API, put builders on independent changes, and bring a reviewer into the conversation when the diff is ready. Open any worker's transcript, ask a follow-up, or redirect it while it works. You keep the context and decide what to test, accept and ship.
Workers share your checkout by default, so their edits are available immediately. For an experiment that needs its own patch, choose a Git worktree and apply the result explicitly. The same runtime runs JSON jobs, serves authenticated terminal clients and opens saved conversations after the host closes.
pi install npm:pi-pinata
Install · Quick start · Fleet · Workflows · Agents · Configuration · Tools · CLI · API · Implementation · Development
Why Piñata
- Delegate independent work in parallel and add workers as the task develops. Dependency graphs stage investigation, implementation and synthesis in one request.
- Keep a worker's conversation and workspace across follow-ups. Each assignment retains its own result, so later answers preserve the original evidence.
- Browse the fleet and message workers directly from Pi. Navigation and direct controls do not need a coordinator model turn.
- Choose shared work for immediate collaboration or worktrees for explicit patch selection. Dirty starting content, manual commits and partial work stay inspectable.
- Define specialists in Markdown or TypeScript. Select their models, tools, skills and output schemas through the same launch contract.
- Inspect failed and cancelled work, recover an original supervised attempt, and reconcile interrupted patch application with its journal intact.
- Use one command and event model from Pi, the headless CLI or your own host.
Piñata handles orchestration. Your coordinator owns the workflow, including testing, review and acceptance. The shipped scout, builder and reviewer are editable agent definitions. Compose your own roles around the work you actually do.
Capability index
| Capability | Guide and reference |
|---|---|
| Parallel workers, staged graphs and dynamic additions | Workflows |
| Steering, follow-ups, continuation and cancellation | Control a worker |
| Fleet tree, transcripts, drafts, search and controls | Fleet and conversations |
| Direct addressed input and explicit retry | Agent mentions |
| Shared files, worktrees, snapshots and dependency inputs | Workspaces |
| Incremental application, rollback and writer exclusion | Apply isolated work |
| Markdown agents, typed reports and frozen skills | Custom agents |
| Models, reasoning, fallback candidates and tool providers | Configuration |
| Filtered parent context and memory revision checks | Context and memory |
| Scoped child delegation and session triggers | Nesting, schedules |
| In-process sessions, supervised processes and Herdr | Backends and lifetimes |
| Finite jobs, live control and saved-run inspection | Headless operation |
| Tools, schemas, commands and exports | Tools, CLI, API |
| Authenticated IPC, durable events and usage | Events and IPC |
| Recovery and trust | Recovery, trust |
| Source map, platforms and verification | Implementation index, platforms, development |
Install
Use Node 22.19 or later and an installed Pi host:
pi install npm:pi-pinata
Restart Pi to load the extension, the subagents skill and five prompt templates:
/scout, /builder, /reviewer, /pinata-review and /pinata-fix. Choose an
available provider/model in Pi. Workers use it unless a definition, configuration
or request selects another model.
For the standalone pinata command, install the package and Pi host peer in the
same npm prefix:
npm install --global pi-pinata @earendil-works/pi-coding-agent@1.5.0
pinata help
Pi's managed extension install supplies host APIs inside Pi; it does not add the
standalone CLI to your shell's PATH. From this checkout, use
node bin/pinata.mjs instead of pinata after npm ci --ignore-scripts.
Piñata targets Linux, macOS and Windows. See platform prerequisites for worker launchers and the Windows compiler requirement. Running workers uses provider tokens. Schema discovery, input validation, ordinary tests and offline arena controls make no model calls.
Let active work settle or cancel it before removal, then use
pi remove npm:pi-pinata (or the exact registered local path). For the global CLI,
use npm uninstall --global pi-pinata. Project files and run evidence remain
after package removal.
Quick start
Open a project in Pi and type:
@scout Map this project's entry points, data flow and tests.
Piñata starts the read-only scout and reports its assignment. Shift+Down opens the fleet; Enter opens the selected conversation. Shift+Left/Right changes workers; Shift+Up returns to the fleet and then to Pi.
After the scout finishes, send:
@scout Follow the parser's error path and identify missing tests.
This creates another assignment in the same conversation. The first result remains available. During active work, addressed input steers the current assignment instead.
For implementation, ask the coordinator for a concrete change:
Use Piñata builders to implement the parser and command help in parallel.
Agree on the parser interface first. Inspect the diff and run the project checks.
The coordinator can add workers, request review, send follow-ups or finish directly. Shared edits are already in the checkout. Completion means execution ended; the coordinator decides whether the change is ready.
For a finite job, start with examples/job.json:
pinata run examples/job.json --workspace /path/to/project --model provider/model --json
Replace provider/model with an available model. Output names the run directory
and exact assignments. pinata status RUN_DIRECTORY --saved --json reads retained work.
Fleet and conversations
The coordinator shows a compact Agents tree while work is active or queued.
Workers have activity markers, model labels and execution state; real children
branch beneath their parent. Dependencies appear as after: references. Failed
or interrupted work that needs attention stays visible. /pinata opens an idle fleet.
Fleet and conversation screens fill the terminal. Pi renders user cards, Markdown, syntax highlighting and tool output using its theme. Completion cards show "Subagent result" with the worker, outcome, summary and conversation command. Backend and context metadata remain available through the details control.
The fleet keeps the animated tree, with one aligned row per assignment and status
near the agent name. The animation travels along active branches; reduced and off
motion settings keep the branches still.
Queued stages are visible by default and show what they are waiting for. Q hides
other queued stages while keeping the selected agent visible. Search with /,
apply with Enter, cancel with Escape, or clear with Ctrl+U. Cancelling restores the
previous query and selected agent. Dependency edges do not create parent/child
branches. Shared model settings appear once;
mixed models get their own column when there is room. The selected agent's task
and waiting reason appear in a separate panel, with d for execution details.
Task previews and the selected panel expand to the terminal width. The Observed
column measures time since this viewer witnessed an assignment start; a dash means
the start is unknown. It is not an estimate of total execution time.
| Input | Action |
|---|---|
| Shift+Down | Open the fleet, including from a worker conversation. |
| Shift+Up or Escape | Return to the parent screen. |
| Up/Down, Enter in the fleet | Select an assignment, then open it. |
| Shift+Left/Right | Select the previous/next worker. |
| Alt+Left/Right | Browse earlier/later assignments for that worker. |
| Left/Right in the fleet | Collapse/expand a child branch. |
| Mouse click/double click | Select/open a fleet row. |
| Mouse wheel, PageUp/PageDown, Alt+PageUp/PageDown | Scroll the selected view. |
/ in the fleet |
Search workers/tasks; Enter keeps the filter. |
f, h, Q in the fleet |
Cycle all/active/failed, include history, show queued work. |
a in the fleet |
Show stop actions for an assignment, agent or entire fleet. |
d in the fleet |
Expand details for the selected assignment. |
| Tab in a conversation | Switch composer/transcript focus. |
| Ctrl+O in a conversation | Expand/collapse tool output. |
| Ctrl+T in a conversation | Show/hide thinking output. |
d, m with transcript focus |
Expand details, load older pages. |
| Ctrl+X | Stop this agent's active/queued work, including children. |
| Ctrl+B | Release a foreground wait; agents keep working. |
| Ctrl+R | Recover the selected original assignment. |
| Ctrl+L | Refresh, or reconnect a disconnected attachment. |
| Ctrl+Left/Right | Cycle main, fleet and conversation screens. |
? |
Show help for the current screen and focus. |
Conversations follow the latest saved output. Scrolling away from the bottom holds history still; reaching the bottom resumes following. Visible live conversations refresh once per second while following. Reading mode, hidden views and saved sources do not poll. There is no separate follow toggle.
Short conversations sit above the composer. Message controls stay at the bottom when output, send feedback or terminal size changes. "Message queued" means the runtime saved your message; it does not confirm the worker has read it. Receipt IDs remain in the expanded Subagent update card in the coordinator conversation.
Each worker keeps its own message draft, separate from the coordinator draft.
Tool and thinking toggles work from either focus and use Pi's configured shortcuts.
They leave the message draft and assignment details unchanged. Standalone viewers
use Ctrl+O and Ctrl+T; ui.keys.expandTools and ui.keys.toggleThinking override them.
A control draft clears only after durable acceptance. The composer labels whether
text will steer, follow up or continue. Question marks inside a draft remain text.
Ctrl+Left/Right, Ctrl+B and Ctrl+R retain editor behavior while composing; use Tab
for transcript controls or the commands below. Help and back controls stay visible
on narrow terminals; less common shortcuts remain in ? help. Help and definition
previews support arrow and page scrolling. Shortcut overrides must use a modifier
or function key and preserve the reserved navigation and editing keys.
Saved and disconnected views label their control limits. The actions menu explains unavailable controls and keeps inspection available. Message drafts remain editable for local commands; messages require a control connection. Earlier assignments identify when you need to select the latest assignment to send a message. Invalid navigation and replacement commands keep the draft and show usage or target errors.
Inside a worker screen, /worker <id> [assignment], /assignment <number>,
/fleet and /main navigate. /steer <text>, /follow-up <text> and
/continue <text> select the message action. /stop, /stop-assignment and
/stop-run stop the selected agent, one assignment, or the entire fleet in the
current run, respectively. Stops include child agents, interrupt active work and
cancel queued work. The fleet action includes hidden and filtered agents and works
without a selected row. Stop requests retain output and file edits; cleanup may take
time. Active schedules can still start new work. /background releases the
coordinator's foreground wait while agents keep working. Esc closes the actions
menu or leaves the view without stopping work.
Other controls are /inspect, /more, /refresh,
/recover, /reconnect, /background, /queued, /scroll up|down|N,
/palette <name> and /help. /replace <new-worker-id> <task> creates a worker
with the selected assignment recorded as origin.
Agent mentions
Only leading user input is routed. A worker ID is its handle; when no worker matches, an exact definition name starts one.
| Input | Behavior |
|---|---|
@scout Trace the parser |
Start the definition, steer pending work or follow up after completion. |
@scout |
Navigate without launching work. |
@run-prefix:scout Inspect errors |
Select a visible run explicitly. |
| An ambiguous handle/prefix | Show candidates and retain the draft. |
| Embedded handles, quoted text, paths and file suffixes | Ordinary Pi input handling. |
The selected run wins when it contains that worker; otherwise Piñata looks for a unique visible match. Unknown definitions produce an error. Model output never becomes a mention command.
Piñata records the routed envelope before dispatch. If its reply is uncertain,
use the displayed /pinata-retry run-prefix:input-token in the original Pi session
and run. It reuses the request, message and target. Repeated callbacks for one
input deduplicate; submitting identical text again is a new request.
See recovery for session loss and retained envelopes.
Palettes and accessibility
/pinata-palette <name> or ui.palette selects a palette. The default, inherit,
uses Pi's theme; the standalone viewer has its own fallback. Available names:
inherit pinata terminal-default
catppuccin-mocha catppuccin-latte
gruvbox-dark gruvbox-light tokyo-night-night
rose-pine-main rose-pine-dawn dracula
nord one-dark one-light
solarized-dark solarized-light
Palette changes preserve worker state and drafts. NO_COLOR, TERM=dumb and
terminal-default disable palette escapes. PINATA_ASCII=1 or TERM=dumb selects
ASCII glyphs. ui.motion accepts full, reduced or off; reduced/off use static
activity markers. Worker text is sanitized for terminal escapes and bidirectional controls
while preserving ordinary Unicode text.
mode: "lean" keeps activity compact; observe adds excerpts. Both retain
conversations. Context utilization comes from a timed source observation or stays
unknown. Cumulative tokens do not establish current context size. Elapsed values
labeled observed cover starts witnessed by that UI.
Workflows
A worker has a frozen definition, model, tools, grants and workspace. An assignment
is one task or follow-up in its conversation, identified by {run, worker, assignment}.
Use references returned by receipts for results, dependencies and patches.
Parallel work and staged graphs
Send this batch to the coordinator's pinata_run tool:
{
"requestId": "parser-team/start",
"command": {
"kind": "create",
"workers": [
{
"id": "parser",
"agent": "builder",
"task": "Implement the agreed parser in src/parser.ts. Preserve unrelated edits."
},
{
"id": "help",
"agent": "builder",
"task": "Update src/help.ts for the agreed parser interface."
},
{
"id": "review",
"agent": "reviewer",
"after": ["parser", "help"],
"task": "Review the parser and help changes. Report defects with source references."
}
]
}
}
Use kind: "add" to extend a run. Acceptance validates the whole batch atomically,
including forward references and cycles. Rejected batches reserve no worker names.
dynamic-shared.json is a runnable starting point.
after binds a worker ID to its latest accepted assignment when the dependent is
accepted. An explicit full reference selects an earlier assignment. Later follow-ups
do not retarget edges or reopen downstream work. Dependencies become ready when
exact predecessors end with confirmed cleanup; reported blockers or failed checks
do not create an acceptance gate.
For report pipelines, declare known stages together. Workers receive previews and
pinata_dependency to list/read complete frozen result JSON. Use the returned
next as the next offset. List offsets count entries; result offsets count UTF-16
code units. Previews show at most eight short summaries. Pages fit within 32 KiB
and retain artifact references without reading their contents. Use inputs for
workspace snapshots and context for explicitly quoted report text.
Control a worker
| Action | Use |
|---|---|
steer |
Send instructions to active or queued work. |
follow_up |
Queue another assignment in the same conversation/workspace. |
continue |
Explicitly resume stopped/failed work with recoverable context. |
cancel |
Stop an assignment, worker or run; descendants default on. |
background |
Release a foreground wait while work continues. |
capacity |
Change active assignment capacity, including descendants. |
A follow-up queues FIFO and keeps earlier results immutable:
{
"requestId": "parser-team/error-cases",
"command": {
"kind": "follow_up",
"worker": "parser",
"messageId": "parser/error-cases",
"text": "Add the agreed malformed-input case and report the checks you ran."
}
}
Capacity defaults to unbounded. Set execution.maxConcurrent to a positive integer
to queue ready work; null removes the limit. Lowering it leaves active work running.
Aborting a wait releases the caller without cancelling workers. Cancellation retains
partial output and checks cleanup separately.
Durable acceptance, backend transport and worker consumption are separate observations. After a lost reply, inspect the original receipt before retrying with the same request ID and payload. Changed payloads under accepted IDs conflict.
Build Relay
Relay parses JSONL service events, summarizes them and renders a Markdown report. The example exercises parallel modules and dependent integration:
scout -> parser -----+
-> summarizer -+-> CLI integration -> reviewer
-> renderer ---+
Create a disposable checkout; setup refuses an existing destination:
node examples/relay/setup.mjs /tmp/relay-exercise
cd /tmp/relay-exercise
Read REQUIREMENTS.md and select available models in job.json. The starter has
unfinished stubs, so its verifier initially fails. Run the job, then inspect it:
pinata run job.json --workspace . --watch --json
node --test
node verify.mjs
git diff
One scout maps interfaces, three builders implement modules, a CLI builder integrates those exact assignments, and a reviewer reports findings. These are example choices. The coordinator chooses checks and follow-ups. Preserve the independent expected report and verifier while repairing implementation. Shared work needs no apply step; Piñata does not automatically commit it.
Workspaces
| Mode | Behavior |
|---|---|
shared |
Work in the authorized checkout, including non-Git projects. Edits are immediately visible. |
worktree |
Start from a frozen Git base and apply a selected isolated snapshot explicitly. |
Tool access and workspace mode are independent. Shared observations can include
user and other worker edits; they do not establish exclusive authorship.
Shared writers and isolated workers retain snapshots when capture succeeds.
Shared readers retain conversations and results without an automatic checkout scan.
Set observeWorkspace: true at creation to request that observation too.
changes returning source: "unavailable" does not mean the checkout was unchanged.
Snapshots record their observation interval and scope.
New worktrees include dirty starting content by default; includeUncommitted: false
selects committed content. Follow-ups keep the worktree; changing modes needs a new
worker. Manual commits, HEAD and index survive capture. Normal isolated completion
can create a private checkpoint ref without moving HEAD or the user's index.
pinata.checkpoint: none disables it. Failed/cancelled work stays available without
a completion checkpoint.
Dependency inputs and setup
inputs composes exact immutable predecessor snapshots into an isolated destination.
Pair it with appropriate after ordering. Missing requested snapshots fail preparation
with retained evidence. For reports, use the dependency tool or quoted context.
The source checkout's .worktreeinclude selects ignored local regular files, such as
.env.local or local/**. Piñata freezes their identities at acceptance and copies
them during initial preparation. Changed sources, links and unsafe paths are refused
or recorded as omitted; existing destination files and follow-up edits are preserved.
Included contents are excluded from snapshots and application even if ignore rules
change. Limits are 1,000 files and 16 MiB per file. Use setup for dependencies.
Setup runs only in writable isolated work. setup: false disables it; a string
selects a shell command; omission can detect a root lockfile and package manager.
Shell capability is required. Detected setup without that grant is skipped with a
reason; an explicit command is rejected. passEnv selects safe environment names;
values are supplied at execution and are not serialized. Success is retained for
the relevant worker and input identity. Failed setup preserves partial edits; inspect
uncertain interrupted setup before deciding to run it again.
Apply isolated work
isolated-follow-up.json starts an experiment. Keep a live owner for continued control:
pinata serve examples/recipes/isolated-follow-up.json --workspace /path/to/project --model provider/model --json
In another terminal, use the printed directory and returned assignment numbers:
pinata changes RUN_DIRECTORY experiment 1 --json
pinata follow-up RUN_DIRECTORY experiment "Add the agreed boundary case" --request-id experiment/follow-up
pinata wait RUN_DIRECTORY experiment --json
pinata apply RUN_DIRECTORY experiment 2 --destination /path/to/project --request-id experiment/apply
Inspect the accepted operationId and its outcome. Later applications deliver the
delta from the last applied snapshot. Apply checks paths, objects, destination
identity and expected content. A checkout-level gate allows multiple shared writers
and excludes application while writable work is active. Unconfirmed cleanup keeps
that exclusion.
Each application journals before and after blobs and cursor meaning. rollback reverses
only matching destination content; later edits conflict. reconcile selects the
retained operation and owner epoch. Preserve journals, worktrees and artifacts until
the outcome is understood. See recovery.
Custom agents
| Shipped agent | Tools | Purpose |
|---|---|---|
scout |
read, grep, find, ls |
Trace code, data flow and risks. |
builder |
read, write, edit, bash |
Implement a concrete task and report actual checks. |
reviewer |
read, grep, find, ls |
Find defects and missing evidence in a supplied change. |
Save a specialist as .pi/pinata/agents/api-scout.md:
---
schemaVersion: 1
name: api-scout
handoffDescription: Map API behavior and compatibility risks.
tools: [read, grep, find, ls]
pinata:
access: read
color: accent.cyan
---
Trace the assigned API from callers to implementation and tests. Return source
references, compatibility concerns and unresolved questions. Do not edit files.
Validate with pinata agents show api-scout --workspace . --json, then use
@api-scout Trace authentication errors in Pi or "agent": "api-scout" in a job.
Discovery and fields
Definitions resolve from lowest to highest precedence:
- Packaged
agents/. <Pi agent directory>/pinata/agents/, normally~/.pi/agent/pinata/agents/.- Project
.pi/pinata/agents/.
Repeatable --definitions <directory> replaces the list in supplied order. Invalid
selected overrides fail with path and field errors. Names match filenames and contain
a lowercase letter followed by up to 31 lowercase letters, digits or hyphens.
agents list|show <name>|validate --json returns source paths and digests.
| Field | Type/default and meaning |
|---|---|
schemaVersion |
Required 1 in Markdown. |
name, handoffDescription |
Stable handle and discovery description. |
| Markdown body | Instructions; project instructions are discovered each assignment. |
model |
Optional exact provider/id. |
modelSettings.reasoning.effort |
off, minimal, low, medium, high, xhigh, max. |
tools |
Default []; named tools checked against host grants/classifications. |
handoffs |
Default []; allowed child definition names. |
outputType |
Registered schema name, or a Zod schema in trusted TypeScript. |
pinata.access |
read by default; write requires a grant. |
pinata.workspace |
Default shared; explicit declaration overrides run workspace config. |
pinata.skills |
Default []; files/directories relative to the definition source. |
pinata.fallbacks |
Default []; up to five model candidates. |
pinata.color |
accent.blue/cyan/green/yellow/orange/red/pink/purple or six-digit hex. |
pinata.inheritContext |
Default none; parent requests a filtered checkpoint. |
pinata.memory |
Default { "scope": "none", "access": "read" }. |
pinata.delegation.maxDepth |
Default 0; requires handoffs and a scoped host grant. |
pinata.checkpoint |
completion private isolated checkpoint or none. |
Acceptance freezes the definition, model, grants, tool providers, skills and workspace
plan. A new worker adopts edited frozen resources; follow-ups preserve the accepted
worker. Project AGENTS.md discovery refreshes each assignment, including new or
renamed files in non-Git projects.
Only explicitly selected skills load. Piñata freezes source and resources and materializes
those bytes for execution. Skills cannot broaden tools or grants. Custom extension
providers are trusted host code; their frozen source identity is checked before load.
pinata agents validate also checks selected skill files and resources without
opening a runtime or making model calls. Missing skills report the resolved path
and pinata.skills field. Validation does not establish model or tool availability.
Typed definitions and reports
For a typed report, export an agents array from an explicit bootstrap module:
import { defineAgent } from "pi-pinata";
import { z } from "zod";
export const agents = [
defineAgent({
name: "api-auditor",
handoffDescription: "Map API compatibility risks.",
instructions: "Inspect the requested API and report concrete findings.",
tools: ["read", "grep", "find", "ls"],
outputType: z.strictObject({
files: z.array(z.string()),
risks: z.array(z.string()),
}),
}),
];
pinata agents show api-auditor --bootstrap ./pinata-agents.ts --json
pinata run job.json --bootstrap ./pinata-agents.ts
defineAgent defaults schemaVersion to 1. Typed and Markdown definitions cannot
share a name. The optional submit_result worker tool accepts:
{
"summary": "Traced authentication errors.",
"data": { "files": ["src/auth.ts"], "risks": ["Missing expiry test"] },
"assessment": {
"findings": ["Expiry behavior has no regression test."],
"claimedChecks": [],
"blockers": []
}
}
outputType validates data. Zod refinements, including asynchronous refinements,
run in the original trusted validator. Discovery exposes input JSON Schema, which
cannot recreate callbacks. Transforms and non-JSON types are rejected. Failed optional
reports remain as reporting errors; ordinary final text remains valid output.
Workers freeze the selected bootstrap path, source bytes, digest and schema descriptor.
Missing or changed bootstraps fail before execution. Editing an already loaded module
requires a fresh host process and worker. Only host code or explicit --bootstrap
can select executable definitions.
Configuration
Layers resolve from personal <Pi agent directory>/pinata.json, project
.pi/pinata.json, then run config. Omitted fields preserve lower layers. models,
fallbacks and toolProviders merge by entry; other supplied fields replace the
corresponding field. Worker overrides resolve into frozen launch data.
{
"schemaVersion": 1,
"workspace": "shared",
"backend": "in-process",
"mode": "observe",
"models": {
"builder": { "provider": "provider", "id": "model", "thinking": "high" }
},
"execution": { "maxConcurrent": 4 },
"ui": { "palette": "catppuccin-mocha", "motion": "reduced", "keys": {} }
}
| Field | Default/behavior |
|---|---|
schemaVersion |
1. |
workspace |
shared; optional worktree. |
backend |
in-process; optional process, herdr-pi. |
models |
{}; per-agent model objects and optional default. |
fallbacks |
{}; per-agent/default lists of up to five available-model candidates. |
execution.maxConcurrent |
null (unbounded), or a positive active-assignment limit. |
mode |
lean; observe adds activity excerpts. |
toolProviders |
{}; named {extension, tools} providers. |
webExtension |
Optional provider for web_enable, web_search, fetch_content, get_search_content. |
passEnv |
[]; safe setup environment names, with values excluded from evidence. |
setup |
Omitted: detect root locked dependencies; false: disable; string: explicit command. |
worktreeRoot |
Optional host worktree location. |
codemode |
true; Pi coding wrapper with underlying capability checks. |
includeUncommitted |
true; include dirty content in a new isolated base. |
ui.palette, ui.motion, ui.keys |
inherit, full, {}. |
Model precedence is worker request, per-agent config, definition, default config,
then parent model. Definition reasoning defaults to off when its model is selected.
Fallback candidates resolve before launch and retain provenance; execution failure
does not silently rerun on another model. Model objects contain provider, id and
thinking. Definition discovery alone does not promise model availability.
Key actions are openFleet, parentScreen, previousScreen, nextScreen,
previousWorker, nextWorker, previousAssignment, nextAssignment, stop,
background, recover, refresh, scrollUp and scrollDown. Invalid/reserved or
duplicate bindings fail explicitly; screen help reflects configured bindings.
Built-in read, grep, find, ls have read access. write, edit, bash and
powershell need write access. Custom providers default to write classification
unless host code classifies them otherwise; known web-provider tools are read-oriented.
Requests must fit the frozen tool list and grant. Loader and control environment names such
as NODE_OPTIONS, LD_PRELOAD, DYLD_INSERT_LIBRARIES and PINATA_* are refused
in passEnv.
Host grants
Authority comes from the host, separately from project config and model requests. The default host grants ordinary read/write tools and both workspace modes. Inheritance, memory and delegation default off.
In Pi, /pinata-grants <json> followed by /pinata-new selects grants for a new run.
Personal <Pi agent directory>/pinata/grants.json supplies persistent grants across
workspaces; command and integration grants take precedence. Invalid personal grants fail
with their path. Accepted workers keep their grants.
Headless hosts use --grants <file>; integrations use HostOptions.grants. Inspect
pinata.grants.v1 before editing it. The grant recipe
contains every required field. Project config, skills, reports and memory cannot
grant authority.
Context and memory
pinata.inheritContext: parent requests a filtered parent conversation. It requires
a host grant, supporting backend and available checkpoint. Pi can supply the current
parent branch; an independent headless host without parent context reports unavailable.
Piñata retains supported messages, complete tool pairs and compaction provenance,
excluding host controls and incomplete pairs. Import does not replay tools. Use task
text or context for explicit quoted background.
| Memory scope | Use |
|---|---|
none |
Default; no memory tools. |
worker |
Retained notes for one worker. |
project-agent |
Notes for one agent within one canonical project. |
The host must grant the same scope and requested access. There is no cross-project
global profile. pinata_memory_read returns a revision; edit/clear requires that
revision. Concurrent writes conflict: read, merge and retry. Clear records an empty
revision while preserving history. Model-written notes remain subordinate to current
instructions and grants.
Pi provides direct controls without a model turn:
/pinata-memory inspect scout
/pinata-memory edit scout {"expectedRevision":0,"text":"Retained notes","data":{"checked":true}}
/pinata-memory clear scout 1
Use the inspected revision. Inspection reports scope, value and provenance; edit replaces
the value, with optional data. These controls use the accepted worker's scope and
write grant, accept no path override and preserve a newer editor draft.
Nested delegation
Enable nesting when a worker should divide a workstream and synthesize its children.
Independent tasks can remain siblings. Child ancestry comes only from the bound
pinata_children tool; after, names and origins do not establish parentage.
The delegator definition,
authorized job and
grants form a complete opt-in example. In Pi, copy
the definition to .pi/pinata/agents/delegator.md, paste the grant JSON into
/pinata-grants, then use /pinata-new. For persistent personal setup, use
<Pi agent directory>/pinata/agents/ and pinata/grants.json.
From this checkout:
pinata serve examples/recipes/authorized-nested.json --workspace /path/to/project --model provider/model --definitions agents --definitions examples/agents --grants examples/recipes/grants.json --json
The parent needs positive depth and allowed handoffs. The host grant must permit
depth, child definitions, tools and workspaces, and the backend must support nesting.
pinata_agents reports effective delegation.enabled and each parent's usable scope
or disabled reason. Plan from that discovery. Model config alone does not create an
agent or enable delegation.
Children use their definitions within the parent's ceiling. Read-only parents cannot grant write access. Child tools control only descendants and never receive a root source or IPC token. IDs share the run namespace; prefix them by workstream and batch independent children in one add call.
Descendants share root assignment capacity. A waiting parent occupies a slot. At
capacity one, pinata_child_wait returns capacity_blocked: finish the parent with
child references, let children settle, then have the coordinator follow up with that
same parent and exact after references for synthesis. dependency_blocked also
requires ending the parent. Polling cannot free its slot. Normal parent completion
leaves accepted children running; cancellation includes descendants by default.
Completion notices do not automatically start another parent turn. The
shipped nested instructions contain exact patterns.
Schedules
A live session can own a trigger that creates ordinary assignments. Creating or updating a schedule returns a paused preview; resume explicitly after inspecting the target and next occurrence. Every accepted occurrence can spend provider tokens.
| Trigger | Shape |
|---|---|
| One-shot | { "kind": "at", "at": "2026-12-25T09:00:00.000Z" } |
| Interval | { "kind": "interval", "milliseconds": 300000 } |
| Cron | { "kind": "cron", "expression": "0 9 * * 1", "timezone": "America/New_York" } |
Cron uses five deterministic fields and an explicit IANA timezone. Aliases and randomized
H fields are rejected. Missing spring-forward wall minutes are skipped; a repeated
fall-back minute runs once. Intervals use a monotonic clock.
Submit scheduled-follow-up.json with
pinata execute RUN_DIRECTORY <file>, then activate it with a new envelope:
{
"requestId": "example/schedule/resume",
"command": { "kind": "schedule", "schedule": { "action": "resume", "id": "inspect" } }
}
pinata_schedule supports create/update/list/pause/resume/delete. Targets select an
existing worker or frozen template. Overlap defaults to skip; queue is explicit.
Closing the owner stops future triggers. A crash or reload leaves schedules paused with
no catch-up. These triggers depend on a live session and do not run as OS cron jobs.
Backends and lifetimes
| Backend | Execution |
|---|---|
in-process |
Pi sessions in the host. Default; avoids worker process startup and releases idle sessions after persistence. |
process |
The same Pi adapter in a supervised Node worker with durable transport and original-attempt reattachment. |
herdr-pi |
The process protocol plus an owned Herdr monitor pane; requires an available Herdr session. |
Backends share the frozen contract for tools, context, skills, memory, continuation and workspaces. Unsupported capabilities fail before launch. Missing Herdr does not switch the request to another backend. Its monitor displays observations; steering uses Runtime controls. Pane and terminal identity checks preserve other panes on close.
lifetime: "attached" stops with a lost owning host. surviving needs a supporting
supervised backend and preserves the original attempt after unexpected host loss.
Explicit shutdown stops both lifetimes. Waiting and lifetime are independent.
Recovery inspects the retained attempt rather than creating an empty conversation.
Payload exit does not establish descendant cleanup or complete output. The POSIX supervisor controls its owned group through a live nonce-bound authenticated channel. Windows uses a creation-time Job Object. Stopped receipts require matching ownership and termination evidence. Missing, escaped or ambiguous activity stays unconfirmed and retains writer exclusion. Process output drains and synchronizes separately from payload exit. Each tiny streaming chunk incurs durable transport work, which can affect completion latency.
Headless operation
A schema-1 job has workers and optional layered config:
{
"schemaVersion": 1,
"workers": [{ "id": "map", "agent": "scout", "task": "Map project entry points and tests." }]
}
run waits for finite accepted work, reports results and closes its host. serve
keeps a foreground owner available for new workers, follow-ups and schedules,
without starting a coordinator model:
pinata serve examples/job.json --workspace /path/to/project --model provider/model --json
Use its printed directory from another terminal:
pinata attach RUN_DIRECTORY
pinata status RUN_DIRECTORY --json
pinata follow-up RUN_DIRECTORY map "Inspect error handling" --request-id map/errors
Git projects use a store under the common Git directory; non-Git projects use
.pinata/runs. --directory selects an explicit run store. Canonical workspace
identity handles aliases consistently.
After the owner closes, use --saved for status, results, conversations and changes.
For live control, explicitly pinata serve --directory RUN_DIRECTORY and inspect
recover. view --control requests a control attachment; saved sources cannot
mutate. Isolated results still need explicit application.
Tools
Pi supplies tool schemas automatically. Start with pinata_agents for definitions
and effective capabilities. Inspect installed contracts with pinata tools --json,
pinata schema <name> --json or the generated JSON catalog.
| Coordinator tool | Input/purpose |
|---|---|
pinata_run |
command, optional requestId; create/add/follow-up/continue/capacity/background. |
pinata_steer |
worker, messageId, text, optional requestId. |
pinata_cancel |
scope, applicable worker/assignment, optional descendants, requestId. |
pinata_wait |
Optional workers or exact targets, and next. |
pinata_status |
Canonical query, default status. |
pinata_agents |
Discover definitions, model defaults and effective grants. |
pinata_changes |
Exact ref; inspect retained workspace changes. |
pinata_integrate |
Exact ref, destination, optional requestId; observe its application operation. |
pinata_rollback |
operation, destination, optional requestId. |
pinata_schedule |
schedule action, optional requestId. |
pinata_recover |
Optional exact targets; inspect/reattach original attempts. |
pinata_page |
cursor returned from a paged tool reply. |
| Worker tool | Availability |
|---|---|
submit_result |
Optional report; outputType can refine data. |
pinata_dependency |
Exact frozen dependency results only. |
pinata_children |
Explicit scoped delegation grant. |
pinata_child_status, pinata_child_wait |
Same grant; descendants only. |
pinata_page |
Previously exposed child-tool output in this worker session. |
pinata_memory_read |
Granted worker/project-agent memory. |
pinata_memory_edit, pinata_memory_clear |
Scoped memory write grant and expected revision. |
Coordinator details retain pinata.surface-reply.v1; pinata_page returns
pinata.tool-page-reply.v1. Model content fits within 32 KiB. Larger replies return
immutable UTF-8 JSON fragments with offsets, digest and nextCursor. Pass it as
cursor to pinata_page and concatenate text in offset order. Details, CLI JSON
and immutable evidence retain the full value. Cursors expire with the host and
cannot address arbitrary files or artifacts. After reconnect, re-query the read or
original receipt for fresh pages.
Commands and queries
Envelopes contain requestId, optional expectedRevision and command. Accepted
receipts name the revision, exact assignments, messages and any application operation.
Stale expected revisions reject. Stable IDs make acceptance retries idempotent;
they do not promise exactly-once arbitrary external tool effects.
| Command | Behavior |
|---|---|
create, add |
Atomically accept worker batches; create can include config. |
follow_up |
Queue with worker, messageId, text, optional after/inputs. |
steer, continue |
Address a worker with message ID/text. |
cancel |
Assignment/worker/run scope; descendants default on. |
background, capacity |
Release a wait or select positive/null maxConcurrent. |
schedule |
Manage session triggers. |
apply |
Apply an exact snapshot to the frozen destination with exclusion. |
rollback, reconcile |
Recover a selected journaled application. |
Worker requests require id, agent, task. Optional fields: origin, messageId,
after, inputs, workspace, observeWorkspace, model, backend, lifetime,
context, inheritContext, memory, delegation. origin identifies an earlier
assignment in the same run. It copies no context or files and adds no dependency;
supply these explicitly when replacing work.
Queries: status, config, receipt, result, events, schemas, definitions,
conversation, changes, application, wait. Result/conversation/changes select
ref; receipt selects requestId; application selects operationId. Conversation
paging uses returned cursor; events use since. Unnamed model-facing schema
discovery returns an index. Request a named schema or tool for its fields.
Errors and versions
| Domain error | Meaning |
|---|---|
invalid_input |
Malformed/rejected fields. |
dependency_missing |
Unavailable assignment reference. |
capability_denied |
Request exceeds authority. |
unsupported |
Selected host/backend/resource lacks capability. |
conflict |
Stale revision, changed accepted payload, state or content. |
owner_unconfirmed |
Ownership/cleanup cannot be established. |
context_unavailable |
Retained conversation/resource cannot be restored. |
commit_unknown |
Durable write outcome is uncertain; inspect the receipt. |
Surface errors also include internal_error, protocol_version, unauthenticated,
transport_closed, transport_limit, with an optional field where useful.
| Contract | Version/schema |
|---|---|
| Job/config | 1: pinata.job.v1, pinata.config.v1, pinata.config-layer.v1. |
| Definition | 1: pinata.definition.v1. |
| Manifest | 1: pinata.manifest.v1. |
| Journal | 1: pinata.journal.v1. |
| Event | 1: pinata.event.v1. |
| IPC | 1: pinata.ipc-*.v1. |
| Application journal | 1: pinata.application-journal.v1. |
| Commands/workers/results/surfaces | Named version-1 schemas in the catalog. |
Package and data versions are independent; unknown versions fail explicitly. JSON Schema describes shape. Canonical validation also checks grants, graph references, resource identity and journal consistency. Pi may coerce scalar tool input before validation; CLI and IPC do not.
CLI reference
pinata help prints installed grammar; --json emits one JSON value per line.
Use - for stdin where a file argument is accepted.
| Command | Purpose |
|---|---|
run <job.json> |
Finite job, optional --watch, then close owner. |
serve [job.json] |
Live owner; --directory reopens a retained run. |
attach <directory> |
Terminal viewer or noninteractive/JSON status. |
view <directory> |
Live/saved viewer; optional --control, --palette. |
request <directory> <file> |
Surface call; mutations require --control. |
execute <directory> <file> |
Canonical envelope. |
query <directory> <file> |
Canonical query. |
status, logs |
Observe directory; optional --watch, logs --since <revision>. |
result, conversation, changes |
<directory> <worker> [assignment]; omitted assignment resolves latest at request time. |
wait <directory> [worker ...] |
Freeze exact current assignments; optional --next. |
steer, follow-up, continue |
<directory> <worker> <message>; preserve --request-id after uncertainty. |
cancel <directory> |
--scope assignment, worker or run, with applicable --worker/--assignment. |
background, recover |
<directory>; release wait or inspect original attempts. |
apply |
<directory> <worker> <assignment> --destination <checkout>. |
rollback |
<directory> <operation> --destination <checkout>. |
reconcile |
<directory> <operation> --owner <epoch>. |
bind-checkout |
<checkout> <retained-manifest.json> --reason <reason>; bind verified legacy metadata. |
schema [name], tools |
Discover canonical schemas and tool contracts. |
agents list, agents show <name>, agents validate |
Discover/validate definitions. |
Host options: --workspace, --directory, --run, --agent-dir, repeatable
--definitions, --config <json-file>, --grants <json-file>, --model provider/id,
--thinking <level>, trusted --bootstrap <module>. --factory <module> selects a
trusted test/integration module exporting openHost. Read-only commands use --saved.
Exit codes: 0 successful execution/observation, 1 failed/uncertain execution or
application, 2 invalid/rejected input, 3 cancellation. These describe execution,
separately from code quality. Aborting attached waits leaves workers running;
closing the owner performs shutdown and cleanup.
Pi commands: /pinata, /pinata-main, /pinata-fleet,
/pinata-conversation <worker> [assignment], /pinata-palette <name>,
/pinata-schema <name>, /pinata-grants <json>, /pinata-new,
/pinata-retry <run>:<input-token>, /pinata-memory inspect|edit|clear ....
Package API
Root pi-pinata exports defineAgent, openHost, Runtime, bindRuntime,
AttachedSource, openSavedSource, startIpcServer, ToolPages, toolContentBytes,
exportSchemas, parseSchema, schemaRegistry.
Public types: AgentDefinitionInput, DefinedAgent, Host, HostOptions,
RuntimeOptions, RuntimeSource, AssignmentRef, CallerAuthority,
CommandEnvelope, CommandReceipt, RuntimeEvent, SurfaceCall, SurfaceReply.
| Entry | Boundary/exports |
|---|---|
pi-pinata |
Full root host API; loads Pi SDK. |
pi-pinata/host |
openHost, defineAgent, builtinTools and host types. |
pi-pinata/runtime |
Pi-independent Runtime and port/options types. |
pi-pinata/contracts |
Schemas/types, versions, assignmentKey, canonicalJson, digestValue, DomainError, exportSchemas, parseSchema, schemaRegistry. |
pi-pinata/sources |
RuntimeSource, bindRuntime, callRuntime, surfaceError, SurfaceFailure, ToolPages, toolContentBytes. |
The .mjs bridges install a package-scoped TypeScript loader for node_modules
on supported Node versions; native Pi/Bun uses its own support. Internal paths
are outside the public compatibility API.
Open a host
import { openHost } from "pi-pinata";
const host = await openHost({
workspace: "/path/to/project",
parentModel: { provider: "provider", id: "model", thinking: "off" },
});
try {
const receipt = await host.runtime.execute(
{
requestId: "api/map",
command: {
kind: "create",
workers: [{ id: "map", agent: "scout", task: "Trace public entry points." }],
},
},
host.caller,
);
if (receipt.status === "rejected") throw new Error(receipt.error.message);
console.log(await host.runtime.wait({ targets: receipt.assignments }, host.caller));
} finally {
await host.close("Integration finished");
}
openHost binds workspace identity, definitions, models, adapters and grants. It
returns runtime, caller, source, workspace, directory, resolved config
and asynchronous close(reason). Retained manifests must match the workspace/run.
Close records cleanup, including unconfirmed ownership.
HostOptions accepts workspace, directory, run, sessionId,
agentDir, ordered definitionDirectories, layered config, parentModel,
parentContext, grants, modelRuntime, bootstrapModule, nodeCommand, backends,
tools, notifications, onDiagnostic. These are trusted integration inputs.
The bootstrap can export agents and/or configureModelRuntime(runtime, manifest?).
Injected model runtimes skip the hook in the owner; process workers invoke it for
their runtime.
Runtime and sources
Runtime.open(options) is the command authority. RuntimeOptions supplies a durable
directory and optional manifest, worker and config resolvers, definition discovery, execution
backends and workspace, application port, scheduling owner, notifications and diagnostics.
fault and effectPort are deterministic test seams.
| Method | Contract |
|---|---|
execute(envelope, caller) |
Durably accept/reject; accepted retries return the original receipt. |
query(query, caller) |
Typed state, results, receipts, events, definitions, schemas and observations. |
wait(request, caller, signal?) |
Freeze targets; abort leaves work running. |
subscribe(request, caller, consume) |
History/snapshot then live events; returns unsubscribe. |
recover(request, caller) |
Inspect/reattach original attempts. |
close({reason}) |
Close schedules, stop owned work, report cleanup. |
bindChild(parentRef) |
Authority-bound child port. |
parentCheckpoint(ref, caller) |
Supported parent conversation checkpoint. |
scheduleTick() |
Evaluate injected scheduling clock. |
deliverNotifications(caller) |
Deliver durable notices through host port. |
recordNotificationConsumption(id, artifact, caller) |
Record exact notice consumption with retained checkpoint. |
diagnostics() |
Observer/execution diagnostics. |
dispatch(ref, caller) |
Injected inert test effect port. |
bindRuntime(runtime, caller) binds request, snapshot, subscribe to fixed
authority. RuntimeSource also reports run/capabilities. AttachedSource.connect
uses authenticated IPC with explicit reconnect, connection listeners and close.
Reconnect resynchronizes observations without replaying mutations.
openSavedSource(directory) is read-only. startIpcServer accepts separate
control and viewer sources and returns a link and asynchronous close.
new ToolPages({artifacts, scope}) binds a trusted ArtifactPort and run/worker
scope. format(value) preserves small JSON and stores larger immutable replies;
read(cursor) returns exact UTF-8 fragments. Cursors authorize only already-exposed
output. Paging failures retain original details and recovery guidance without changing
command receipts.
Extension ports
WorkerResolver freezes definitions, grants, loadouts and workspace plans before
acceptance. BackendPort advertises restoration, steering, context, skill, memory,
delegation, fallback, lifetime and cleanup capabilities. Start and reattach handles own
input receipts, observations, completion, checkpoints and stop receipts.
WorkspacePort owns prepare/recover/capture/release, retaining writer registration
until confirmed cleanup. RuntimeApplicationPort performs journaled application
outside command serialization. NotificationPort distinguishes stable notice
transport from model consumption; Pi needs the complete notice in the outgoing
provider payload and a retained parent branch to record consumption. onDiagnostic
observes adapter/subscriber failures. New surfaces should use RuntimeSource
and preserve cancellation, context and recovery through these ports.
Events and IPC
pinata.event.v1 distinguishes durable committed transitions, replay snapshot
projections and transient activity. Revisions order history; ownerEpoch identifies
ownership generations. Activity source and ordinal support deduplication. Subscribers
can coalesce activity or reset to snapshots; activity gaps do not imply missing commits.
History records assignments, effect intent and observation, backend enrollment, input transport and consumption, conversations, usage, output, cleanup, snapshots, settlement, schedules and application. Results and notices use exact assignments, keeping earlier and later results distinct.
Usage reconciles per assignment and attempt with uncached input, output, cache reads and writes and optional cost. Missing or conflicting coverage keeps totals unknown while retaining partial observations. Price estimates, invoices and context utilization are separate.
IPC protocol 2 binds loopback and records run, server and owner identities with separate
viewer and control credentials. A versioned hello supplies role and token. Root control
credentials must stay outside worker transports and model prompts. Exact handshakes,
frames and limits are in pinata.ipc-*.v1.
Transport IDs and durable mutation IDs serve different purposes. After disconnect,
query the mutation receipt. Chunking preserves full replies; bounded queues and
acknowledgments let slow subscribers reset to authoritative snapshots. abort_wait
stops only that wait. Viewers cannot mutate, recover or claim ownership. Invalid frames,
chunks, credentials and versions are refused before dispatch. Notice delivery
receipts deduplicate by stable ID, separately from consumption.
Recovery
Start with status, results, conversations and changes for the exact assignment; use
--saved without an owner. Shared files, isolated snapshots and worktrees remain
inspectable after failure or cancellation.
| Situation | Action |
|---|---|
| Lost live connection | Reconnect to the existing owner. |
| Uncertain mutation reply | Query original requestId, then retry the same payload/ID if appropriate. |
| Uncertain mentioned input | Use displayed /pinata-retry in original session/run. |
| Host died | Explicitly serve --directory RUN_DIRECTORY, then inspect recover. |
| Stopped work has context | continue; otherwise create a replacement with origin/context. |
| Interrupted application | Query the operation and reconcile journal/owner first. |
| Rollback conflicts | Preserve later edits and resolve explicitly. |
recover returns reattached, finalizing, active, unavailable or uncertain.
It never automatically launches a replacement. Missing sessions remain explicit errors.
Checksum-verified mention envelopes sync to RUN_DIRECTORY/mention-inputs/ before
dispatch, even if Pi never saved its first custom-only session. Direct Pi retry
still requires the original session binding. If lost, inspect the retained envelope
and use explicit canonical request recovery; do not infer a new action from text.
Owner claims have exact identities and epochs and never expire by age. Reconciliation needs current proof the selected owner is inactive. Live, remote or uncertain owners stay blocked for their matching host. Preserve before and after blobs, journals, worktrees and cleanup receipts. Deleting ownership files or resetting the checkout discards recovery evidence.
Checkout gates retain a directory creation-time binding when the filesystem provides
one. This allows a device number to change after reboot while preserving the original
manifest and leases. Each running gate still checks the current device, inode and
creation time. Filesystems without creation times retain strict device checks.
Older gates acquire a binding when reopened with their original directory identity. If the device number already changed, inspect the retained manifest and activity and verify that the path still names the original checkout. Then run:
pinata bind-checkout /path/to/project /path/to/project/.pinata/checkout-gate/manifest.json \
--reason "Verified original checkout after device renumbering"
This command requires the exact retained manifest, matching path and inode, an
available creation time, and inactive local owners. It records evidence and preserves
every lease. Retained writers still require normal cleanup reconciliation before
application. An interrupted binding retains a reconciliation fence for inspection.
On Linux, an owner from a different recorded boot is inactive even if its PID has
been reused; a live PID from the same boot remains blocked.
Trust boundaries
The host binds caller identity, workspace roots, tools and grants. Model requests, definitions, skills, memory and output are data validated within those bindings. They cannot replace their caller, select a root token or broaden authority.
Worktrees isolate patches. Shell tools and custom extensions execute with the host account's operating-system permissions. Use an external sandbox for untrusted code. Checkout gates and claims coordinate cooperating processes, without containing hostile same-user mutation.
Durable recording, transport delivery, consumption, execution, worker assessments, reported checks, coordinator acceptance, application and oracle correctness remain distinct. Piñata imposes no automatic reviewer, mandatory ownership allocation, production token or time budget, or verification-dependent access to completed work.
Durability assumes local atomic create and link operations and file fsync. POSIX directory fsync is exercised; equivalent Windows directory durability, machine power loss and network filesystems are outside demonstrated coverage. Torn or corrupt records and ambiguous effects require explicit inspection and reconciliation.
Platforms
| Platform | Prerequisites/validation |
|---|---|
| Linux/macOS | Node 22.19+. CI runs full offline tests, Pi/package smokes and advisory measurements on Node 22.19/24. |
| Windows x64/ARM64 | Node 22.19+ and .NET Framework 4 C# compiler. Targeted CI covers portable contracts/storage, supervision, Pi process workers, package installation and native-host default workers. |
| Herdr | Available local session; unavailable conditional cases are skips. |
Development pins Pi SDK 1.5.0. CI exercises native Pi 1.1.0 compatibility and selects
native Pi 1.5.0 for benchmarks. Native hosts and Node workers are separate environments.
Native Pi launches workers through node on PATH; trusted hosts can select
HostOptions.nodeCommand. A Node Pi host uses its current Node executable.
Windows builds packaged PinataJobLauncher.cs on first use, caching under
%LOCALAPPDATA%\\Pinata\\job-launcher by source/compiler/architecture. A receipt
verifies the executable; no runtime or helper is downloaded. A missing compiler or changed
cache fails launch. Job membership is established during CreateProcessW with
PROC_THREAD_ATTRIBUTE_JOB_LIST and restricted inherited handles. The job handle
is not inherited; kill-on-close owns descendant lifetime.
Executable arguments and Unicode environments are preserved. .cmd/.bat disables
delayed expansion and rejects special shell characters; use an executable for arbitrary
ordinary argument data. Cleanup requires matching empty-and-closed job evidence
and guardian termination. Lost acknowledgments stay unconfirmed. Saved PIDs, job
names and taskkill are not cleanup authority. Job inheritance is not a permission
sandbox.
Windows tests cover creation crashes, nested jobs, descendant-held pipes, arguments, cancellation, reconnect and host loss. Full workspace and application fault matrices and terminal navigation run on Linux/macOS. Test reports identify the actual host and worker runtimes; an executable label alone does not establish runtime coverage.
Architecture and implementation index
Every surface uses one Runtime, scheduler, command authority and event model. Pure contracts and core rules stay independent of Pi and filesystem effects.
flowchart TD
Pi[Pi tools and fleet UI] --> Source[RuntimeSource]
CLI[Headless CLI] --> Source
IPC[Authenticated clients and viewers] --> Source
Source --> Runtime[Runtime command authority]
Runtime --> Core[Pure acceptance and lifecycle rules]
Runtime --> Store[Journal, artifacts and owner claims]
Runtime --> Backends[In-process, process and Herdr]
Runtime --> Workspace[Workspace and application ports]
The journal records acceptance and effect intent before dispatch. Immutable artifacts sync before committed references. Checkpoints and projections speed reads while committed history stays authoritative. Long workspace and application operations stay outside command serialization. Recovery uses saved launch, session and cleanup evidence to reattach the original attempt or report uncertainty.
The source tree groups implementation by responsibility. Regression suites live in
test/engine/ alongside scripted fixtures.
pinata/
|-- engine/
| |-- contracts/ # Schemas, versions, events and public boundaries
| | `-- schemas.ts # Named schema registry
| |-- core/ # Pure acceptance, graph, readiness and lifecycle rules
| |-- runtime/ # Command authority, execution and controls
| |-- storage/ # Journal, replay, artifacts and owner claims
| |-- definitions/ # Markdown and typed agent definitions, frozen loadouts
| |-- host/ # Workspace, models, adapters and grants
| |-- agent/ # Pi sessions, tools, reporting and guards
| |-- backends/ # In-process, process and Herdr execution
| | `-- supervision/ # Process ownership, output and Windows Job Objects
| |-- workspace/ # Worktrees, snapshots, setup and writer exclusion
| |-- application/ # Journaled apply, rollback and reconciliation
| |-- context/ # Instructions, filtering and restoration
| |-- memory/ # Scoped memory and revision history
| |-- schedules/ # Session schedules, DST and overlap handling
| |-- pi/ # Extension tools, mentions, grants and notices
| |-- ipc/ # Authenticated transport and protocol negotiation
| |-- sources/ # Live and saved runtime access, paging
| |-- headless/ # CLI arguments, commands and output
| |-- viewer/ # Standalone terminal viewer
| `-- ui/ # Fleet, transcripts, palettes and drafts
|-- bin/pinata.mjs # CLI launcher
|-- agents/ # Shipped agent definitions
|-- prompts/ # Coordinator and workflow prompts
|-- skills/ # Pi subagent skill
|-- examples/ # Jobs, recipes and Relay integration
|-- docs/reference/ # Generated schema and tool catalog
|-- test/engine/ # Contract, runtime and integration regressions
|-- bench/ # Scripted and loopback overhead measurements
|-- arena/ # Repeatable coding trials and retained evidence
|-- scripts/ # Catalog generation, checks and package releases
`-- .github/workflows/ # CI, npm publishing and package mirror
Development
Use Node 22.19+ and retain the lockfile. Pin direct development dependencies; Pi stays an optional host peer. Read STYLE.md and AGENTS.md before editing source.
npm ci --ignore-scripts
npm run check
npm test
check runs Oxfmt, Oxlint and TypeScript. Format changed files with
npm exec -- oxfmt --write <paths>. The scripted test suite makes no paid model calls.
It runs ordinary files with at most two concurrent files, then exhaustive apply and
rollback fault matrices separately. Test timeouts supervise fixtures, rather than
imposing production worker deadlines.
Focused and integration checks:
node --test test/engine/rewrite-contracts.test.ts
node --test test/engine/rewrite-child-backends.test.ts
node test/engine/pi-smoke.ts
node test/engine/package-smoke.ts
npm run arena -- --offline
node scripts/reference.ts --check
node scripts/public-api.ts --check
Pi smoke uses the public RPC host with a scripted provider. Package smoke packs the
manifest, installs runtime dependencies in a scratch prefix, imports public APIs and
loads the extension, skill and prompts through Pi. PINATA_PI selects an absolute
executable. Test native Pi's default worker launcher with
PINATA_PI=/path/to/native/pi node test/engine/native-backend-smoke.ts.
PINATA_NATIVE_BACKEND_ALLOW_NODE=1 explicitly permits and labels a Node host instead.
The offline terminal driver uses an isolated agent directory, rejects provider requests and retains PTY bytes, events and colored frame HTML:
python3 test/readiness/rewrite-ui-pty.py --root "$PWD" \
--out bench/results/rewrite-ui-unique \
--pi node "$PWD/node_modules/@earendil-works/pi-coding-agent/dist/cli.js"
It checks widths, palettes, streaming, navigation, drafts and disposal. F12 is test instrumentation. Render requests and actual terminal paint are separate measurements.
The Live Pinata/Pi contract workflow uses OPENAI_API_KEY on trusted PRs,
master pushes and manual runs. Fork/Dependabot PRs skip it. It uses
gpt-5.4-mini-2026-03-17, thinking off, at most 256 output tokens and 64 KiB of input
per request. Two assignments permit at most 24 HTTP attempts total, with HTTP retries
disabled. It checks read-only grants, typed reporting, continuation, usage, cleanup
and saved replay. Failed attempts retain request and usage evidence too; credential
directories stay outside uploads.
With token-spend authorization and the key already set, run
node test/engine/live-openai-smoke.ts. Ordinary tests use a fake transport.
Writing and Vale
This README is the maintained guide and reference. Tools consume the separate JSON catalog. Agent definitions, prompts, skills, repository instructions and executable example fixtures remain separate functional inputs. The checked-in code, contracts, tests and evaluation harness define the release behavior.
npm run docs:setup
npm run docs:lint:changed
npm run docs:lint:agents -- --changed
Setup installs checksum-verified Vale 3.26.0, Google 0.7.1 and ai-tells 1.37.0 in
ignored .vale/ directories. It supports x64/ARM64 on Linux/macOS/Windows and uses
system tar. Ordinary scans need no network/model calls.
docs:lint checks reader prose; the agents profile checks spelling, repetition and
terminology in operational inputs. --changed covers staged/unstaged/untracked
prose against HEAD. Include branch commits with
npm run docs:lint:changed -- --base origin/master.
node scripts/vale.mjs --json emits JSON; CI retains both profile reports.
All v1 findings are advisory; missing tools, invalid configuration and execution
errors fail. Inspect output and fix useful findings. Preserve technical qualifications
when a suggestion changes meaning. Review claims against code and use a humanizer
pass to remove filler, inflated claims and repetitive phrasing while preserving
commands, grants, recovery steps and evidence limits. .vale.ini selects rules;
local terminology/vocabulary live under .vale/styles/. Use inline code for identifiers.
Performance
Scripted and HTTP loopback benchmarks measure coordination, storage, sessions and cleanup. Coding quality is evaluated separately.
npm run bench
npm run bench:loopback
npm run bench:process -- --repeat 5 --scenario fanout-1 --scenario fanout-8
PINATA_PI=/path/to/pi npm run bench -- --host tui --scenario ux-8
node --expose-gc bench/viewer.ts --repeat 3
Workloads cover fan-out at 1/8/32/64 workers, dependencies, mixed work and streaming.
Scale probes are scale-queue-1000, scale-fanin-256, scale-batches-8; select with
--scenario. --sweep --workers 1,8 --histories 1,128 controls retained history.
Independent axes: --mode lean|observe, --viewer attached|detached,
--workspace shared|worktree, --backend in-process|process|herdr-pi,
--lifetime attached|surviving.
bench/budgets.json retains accepted named profiles and the 2× CI tolerance.
On controlled hardware, enforce them with:
PINATA_PI=/path/to/pi node scripts/bench-ci.mjs engine --expose-gc bench/run.ts --ci --host pi
Shared-runner CI gates execution, accounting and cleanup and reports timing as advisory.
The wrapper uses os.tmpdir() unless PINATA_BENCH_ROOT selects a root. CI uses
/dev/shm on Linux and runner temporary storage on macOS. It retains complete
successes/failures under bench/results/ with original-path mapping and filesystem
identity. Direct bench/run.ts measures beside --out; --ci does not select a
medium. Comparisons require matching storage provenance and named profiles.
A complete in-process over-budget attempt may retry twice; all attempts remain. Missing measurements, incomplete usage, failed execution or unconfirmed cleanup fail immediately. Process and Herdr timing is advisory without matching baselines. Unavailable backends stay unavailable. Mac/APFS profile selection needs observed filesystem and device proof; Linux limits remain unchanged.
Startup covers committed preparation through the first ordinary workload provider request. Setup, acceptance, command-to-request and dependency dispatch use different boundaries. Compaction requests retain separate identities; full elapsed time, CPU and usage includes compaction. RSS samples are lower bounds with host and child coverage labeled. Collected heap can grow with history; descriptor counts do not establish timer coverage.
The UI target is p95 event/key-to-render-request at most 100 ms with eight scripted workers. Terminal paint is measured separately. Resource probes require stable owned counts and confirmed runtime and supervisor cleanup. The HTTP fixture observes closure of its own idle connections before sampling.
Retain raw cells and incomplete and failed attempts at unique paths. The wrapper forwards interrupts and waits to retain files; SIGKILL cannot run the final copy, so synced command status identifies the original directory. Match source, lockfile, scripts, tools, runtime, hardware, storage and launcher. Compare measurements only when their boundaries and coverage match. No Windows latency baseline is accepted.
Before attributing an arena improvement to runtime optimization, matched profiles must show overhead accounts for at least 10% of the affected path. Smaller changes remain local improvements. Retain source closures, raw attempts and the exact measurement profile with each result.
Coding arena
The arena runs repeatable assignments with independent oracles and complete trial evidence. Offline controls copy or mutate known solutions to test the harness:
npm run arena -- --offline
npm run arena -- --offline --task team-board@1 --workflow parallel-review
node arena/report.ts COMPLETE_BATCH_OR_MATRIX_DIRECTORY
npm run arena:bootstrap
npm run arena:doctor
npm run arena:local -- --agent pinata --preview
The corpus includes Checkout, Reservations, Incidents and Team Board. Task definitions
freeze prompts, starters, solutions and oracle cases. Changes require a new task version. Bootstrap
overwrites generated arena/local/{codex,pi,pinata}.json. Doctor checks executable
startup and configuration; preview prints commands and prompts. Neither establishes model
authentication or availability or spends tokens.
Native and external trials require separate token-spend authorization and
--allow-external, for example npm run arena:local -- --agent pinata --task checkout --allow-external. Requested --concurrency N is an experimental condition
whose meaning can differ across frameworks.
Batches durably record cell IDs, ordering, conditions, limits and source and task digests
before launch. Started cells without published results read interrupted_unknown.
Pending, failed, timed-out and interrupted cells stay in the denominator. An uncertain
active cell needs explicit reconciliation before retry. Reports preserve all attempts
at new paths.
Results separate execution, oracle correctness, capabilities, usage and workflow attestation. Missing, corrupt or conflicting observations keep totals unknown, including missing child records. Outer process supervision does not attest inner planning, delegation or review. Native and external workflow comparison eligibility stays false until independently attested. A passed oracle establishes only tested candidate contracts; it does not establish framework superiority.
Oracles use a separate Node process with filesystem permission flags, fresh IPC, no credentials and a trusted score channel. Candidate stdout cannot supply a score. These are process guardrails. Use independent isolation for untrusted code; network confinement and hostile native-code containment are outside demonstrated coverage.
External configs contain schemaVersion, adapter, condition, optional limits,
validated by adapter schemas. Commands run in the candidate
checkout with an absolute request-file argument. Return one JSON response on stdout;
Stderr and raw streams remain. Requests disclose no private oracle or reference solution
paths. The native Piñata adapter loads the extension with ordinary coding tools
and the shared command API.
Source evidence includes declared harness, runtime and corpus roots, untracked source and package and lockfile configuration, excluding credentials and results. External dependency coverage stays partial. Candidate snapshots are immutable and timed, with capture cost separate. Match task version, starting files, model and settings, prompts, tools, limits, host and workflow; record unmatched differences and order. Results remain bound to the exact code, task, conditions and observed execution recorded for that trial.
Publication
The npm package is pi-pinata, published by funsaized under MIT. Zod/cron-parser
are pinned runtime dependencies; Pi SDK packages are optional host peers. Releases
require authorization for the target commit and version, pushes, tags and publication.
Inspect registry state before choosing a version and keep credentials out of logs.
On an authorized release branch:
RELEASE_VERSION="<version>"
npm version "$RELEASE_VERSION" --no-git-tag-version
npm run check
npm test
node test/engine/pi-smoke.ts
node test/engine/package-smoke.ts
npm pack --dry-run --json --ignore-scripts
Inspect packed metadata, README/LICENSE, engine/public APIs, the subagents skill, five prompts, three presets, examples and the JSON catalog. Exclude run state, logs, tests, credentials and development dependencies. Record checks in the PR. After review and green CI, merge and tag that exact commit:
git fetch origin
git push origin "origin/master:refs/tags/v${RELEASE_VERSION}"
The release workflow checks tests and style, tag,
lockfile, package identity and packed paths, then publishes the exact tarball with
provenance through NPM_TOKEN. Stable tags use npm latest; vX.Y.Z-next.N uses
next and a GitHub prerelease. The token must belong to funsaized and permit
pi-pinata. CI checks integrity before mirroring, then creates the GitHub release
after both registries succeed.
Check npm view pi-pinata name version maintainers --json; for manual publication,
npm whoami must identify funsaized. Do not read credential files. Stop if another
project or account owns the package. Registry absence does not reserve a name.
Publish the inspected tarball with npm publish "./pi-pinata-${RELEASE_VERSION}.tgz" --access public using current authentication/MFA.
Verify registry version/integrity/repository/maintainers, download a fresh tarball,
compare paths and content, install in a scratch prefix and load it through an isolated
Pi agent directory. Exercise pinata help and retain the tested install evidence.
Inspect failed or uncertain publication before retrying. Existing npm versions must
match integrity. Retry an existing tag with gh workflow run release.yml --ref master -f tag="v${RELEASE_VERSION}". Never move published tags or overwrite package
versions. Corrective releases, deprecation, unpublishing and dist-tag changes need
their own authorization.
The mirror workflow downloads npm
artifacts and changes only package name/manifest formatting to @funsaized/pi-pinata.
It verifies contents, executable bits and other metadata before/after publication
with GITHUB_TOKEN package-write access. It neither rebuilds nor publishes to npm.
Repeats verify existing artifacts.
The release calls it immediately; the daily 07:23 UTC job mirrors npm's current
latest. Backfill with gh workflow run sync-github-package.yml --ref master -f version="$RELEASE_VERSION". Only npm's current latest gets GitHub's latest;
older/prerelease backfills use mirror. Existing tags stay unchanged. Mismatches
need investigation, not deletion/recreation. Future upstream publishConfig stops
for review.
GitHub registry installs need authentication even for public packages. Enter a
classic token with read:packages only at the password prompt:
npm login --scope=@funsaized --auth-type=legacy --registry=https://npm.pkg.github.com
pi install npm:@funsaized/pi-pinata
Most users can install from npm. Remove the old source before switching because both copies register identical skills and prompts. New GitHub packages are private by default; visibility changes are separate, and public visibility is permanent. Authentication failures or 404s can mean repository/package access problems, not permission to delete an existing version.
License
MIT.