Developer path

Build and operate workflows

This page describes the package contract from the host tool boundary through the sandboxed workflow worker and its durable run store.

Installation and trust

Use Node.js 22.19 or newer. pi-extensible-workflows is a trusted Pi extension: installing it gives the extension the same filesystem and process access as Pi. Only install it from a source you trust, and open and trust the project in Pi before running doctor.

For source installation and local development, clone the repository and change into its directory first:

git clone https://github.com/vekexasia/pi-extensible-workflows.git
cd pi-extensible-workflows
npm ci
npm run check
pi install "$PWD"

For a one-session trial without changing Pi settings:

pi --no-extensions --extension "$PWD/src/index.ts"

The package declares Pi AI, Pi coding agent, and TypeBox as peer dependencies. The repository development setup currently pins the Pi packages to 0.80.9.

Global and project settings

The optional global settings file is <agentDir>/pi-extensible-workflows/settings.json, normally ~/.pi/agent/pi-extensible-workflows/settings.json. Trusted projects may also use <cwd>/.pi/pi-extensible-workflows/settings.json. If PI_CODING_AGENT_DIR is set, it replaces ~/.pi/agent. Missing files use defaults. Both files use the same strict schema: unknown keys, malformed JSON, and invalid values stop launch or resume with the source settings path in the error.

{
  "concurrency": 8,
  "modelAliases": {
    "reviewer-model": "anthropic/claude-fable-5:high",
    "developer-model": "openai-codex/gpt-5.6-luna:xhigh",
    "cheap-model": "developer-model:low"
  },
  "disabledAgentResources": {
    "skills": ["learning-opportunities"],
    "extensions": ["~/.pi/agent/extensions/review-loop.ts"]
  }
}

concurrency defaults to 8 and accepts integers from 1 through 16. Effective settings use defaults < global settings < trusted project settings < per-run options; a workflow call's concurrency field is highest precedence.

Model aliases

Alias names are case-sensitive and must match [A-Za-z][A-Za-z0-9_-]*. Targets may be concrete provider/model references or other aliases, with an optional recognized thinking suffix. Unknown targets and cycles are rejected.

Aliases work anywhere the workflow runtime accepts a model, including role files. An exact configured alias wins over a native bare model name. A thinking suffix on an alias reference overrides the resolved target's suffix, so cheap-model:low can reuse a model while changing its thinking level. Explicit call-level thinking remains highest precedence. Resolution never falls back: an unavailable target fails with UNKNOWN_MODEL and identifies the alias, resolved target when known, and settings path.

Each launch and resume snapshots one effective alias mapping for that execution segment. A trusted project's modelAliases replaces the global map, including {} to clear it; omitted project fields inherit global settings. Completed and replayed calls retain their results; pending, retried, and new calls use mappings reloaded on resume. Mapping changes produce one persisted warning in run events and the inspector.

workflow_catalog lists effective alias names, kinds, and provenance without probing availability or injecting them into guidance; concrete static and resolved targets remain available through settings, doctor, snapshots, and the inspector. /workflow can list, add, edit, and delete aliases in the active effective settings source. It atomically updates only modelAliases, preserves other settings, and affects new launches and resumes without /reload.

Workflow-agent resource exclusions

Trusted projects can configure every workflow setting in <cwd>/.pi/pi-extensible-workflows/settings.json. Project settings are optional and partial. A supplied modelAliases or disabledAgentResources replaces the corresponding global collection, so empty objects or arrays clear inherited values. Project settings are ignored, including validation, when the project is untrusted. Each selector is a Minimatch-compatible pattern evaluated in declaration order; a positive match disables a resource and a leading ! re-enables it, with the last matching selector winning.

{
  "disabledAgentResources": {
    "skills": ["project-interactive-skill"],
    "extensions": ["../../extensions/project-only-extension.ts"]
  }
}

Skill selectors match declared skill names. Extension selectors match normalized source paths; relative path patterns resolve from the settings file, ~ expands to the home directory, and non-glob path prefixes are canonicalized before glob suffixes are appended. Dot-prefixed path segments are included, and configured selectors retain declaration order so repeated patterns preserve last-match semantics. For example, skills: ["*", "!my-project-*"] disables every skill except project-prefixed ones, while extensions: ["**/*", "!../../extensions/**"] disables all extensions selected by the broad pattern except those under the relative project extensions directory. Missing keys and empty arrays exclude nothing.

Exclusions affect workflow agents only. The parent Pi session keeps its normal resources. Disabled extensions are filtered before their factories run, and disabled skills are removed before prompts and skill commands are registered. New launches use current effective policy; active segments retain their snapshots, while resumes and retries reevaluate trusted project policy. workflow doctor and workflow_catalog report effective values and global/project sources; attempt summaries persist the concrete skills and extensions excluded for that attempt.

Tool API

The extension registers these core tools and one command:

  • workflow runs an inline or registered-function deterministic JavaScript workflow.
  • workflow_resume resumes a budget-exhausted run with an optional budget patch.
  • workflow_retry creates a linked child for an explicitly failed run and replays its completed journal operations.
  • workflow_respond approves or rejects a pending checkpoint or budget decision.
  • workflow_stop stops an active workflow run by ID.
  • workflow_catalog lists registered functions, variables, and model aliases; it is registered only when the active registry has one of these entries.
  • /workflow lists and controls runs in the current Pi session.

workflow parameters

FieldTypeContract
namestringRequired non-empty name for inline launches. Invalid for registered function launches, which use workflow as the run name.
descriptionstringOptional human-readable metadata.
scriptstringImmutable workflow source. Mutually exclusive with workflow.
workflowstringRegistered function by its unqualified name.
argsJSON valueAvailable inside an inline script as args; for a registered function it must satisfy the function input schema. Defaults to null for inline scripts.
foregroundbooleanWait for the final value instead of returning a run ID; the model-visible result also includes the completed run ID.
concurrencyinteger 1-16Per-run active-agent limit.
budgetobjectOptional aggregate soft/hard limits for tokens, costUsd, durationMs, and agentLaunches.
parentRunIdstringOptional terminal run in this project and Pi session for a new launch. Matching explicit named withWorktree scopes reuse its existing checkout; other scopes remain new worktrees. It never replays or resumes a run.

workflow_respond parameters

{
  "runId": "...",
  "proposalId": "...",
  "approved": true
}

Use name instead of proposalId for checkpoint decisions. A budget relaxation proposal remains pending until this tool approves or rejects its exact proposal ID.

workflow_stop parameters

{ "runId": "..." }

Stopping cancels the workflow, descendant agents, and scheduler work before persisting state: "stopped". The result reports stopped: true on success, unknown_run for an unavailable or differently owned run, and already_terminal for a completed, failed, or stopped run.

workflow_resume parameters

{ "runId": "...", "budget": { "tokens": { "hard": 120000 } } }

workflow_retry accepts exactly { runId: string }. The source must be a persisted failed run in this project and Pi session. Failure diagnostics expose that exact source ID, replayable and incomplete paths, artifact locations, and validated named worktrees for direct use with the advertised action. It returns a fresh child ID linked by parentRunId; completed agent, shell, registered-function, and checkpoint operations replay from the parent lineage, while incomplete operations execute normally. Parent usage and budget version carry forward, and concurrent retry children sharing a lineage are rejected. A budget_exhausted source must use workflow_resume({ runId, budget? }); completed, stopped, and interrupted sources return state-specific recovery guidance. Replay does not repeat journaled side effects, but external effects performed before failure are not guaranteed exactly once.

{ "runId": "failed-run-id" }

Background workflow calls return a run ID immediately. Completion or failure is delivered as one follow-up. Foreground calls return the workflow value inline.

Aggregate run budgets

A launch may set run-wide soft and hard limits. Every dimension and threshold is optional; omitted values are unlimited.

{
  budget: {
    tokens: { soft: 100000, hard: 120000 },
    costUsd: { soft: 5, hard: 6 },
    durationMs: { soft: 900000, hard: 1200000 },
    agentLaunches: { soft: 8, hard: 10 },
  },
}

Token, duration, and launch limits are non-negative integers; cost limits are finite non-negative numbers. Zero is valid, and soft must be lower than hard when both are set. The former maxAgentLaunches setting and launch field have been removed; use budget.agentLaunches.

Usage covers all workflow-agent attempts, retries, and descendants but excludes the main orchestrator. Tokens count reported input plus output and exclude cache reads and writes. Accounting uses Pi's cumulative session statistics, so compaction cannot remove earlier token or cost usage from progress, budgets, or persisted attempt totals. Replayed calls retain historical usage without counting as new launches.

A soft crossing is recorded once per budget version. If more model work remains, active agents receive one wrap-up instruction before their next turn and later agents are annotated; a final crossing response completes without an injected message.

Hard launch limits are checked immediately before dispatch. Token, cost, and duration limits are checked between model turns. In-flight turns and tools may finish: a final overshooting response is accepted, while a response requiring tools or another turn stops with BUDGET_EXHAUSTED before that work begins. If accepted results let the workflow return without more model work, the run completes with an overrun event; otherwise it enters the resumable budget_exhausted state.

Duration includes active workflow JavaScript, agent work, and tool calls, but excludes pauses, checkpoint waits, and offline time. Historical usage remains charged after resume.

workflow_resume accepts the exact run ID and an optional budget patch. Omitted patch values stay unchanged and explicit null removes a limit. Tightening applies immediately; raising or removing a limit creates a persisted human-approval proposal. Every exhausted hard dimension must be raised above retained usage or removed. Answer the exact proposal ID with workflow_respond. This control is distinct from workflow_retry, which accepts only failed runs, and from per-agent retries.

/workflow and the inspector show limits, usage, percentages, budget versions, crossings, and overruns. Compact TUI surfaces omit budget rows entirely when a run has no effective limit, including after a patch removes every limit. Required exhaustion, adjustment, and approval context remains visible.

Workflow DSL

Workflow source is plain sandboxed JavaScript. It has no imports, filesystem, network, timer, or dynamic-code globals, and no direct access to the host process. The trusted host-mediated shell() primitive is the explicit exception for command execution:

PrimitiveUse
agent(prompt, options)Run one Pi agent and return its bare text or JSON value.
shell(command, options)Run a platform-shell command and return its exit code, stdout, and stderr.
prompt(template, values)Safely interpolate JSON-compatible values into a prompt.
parallel(name, tasks)Run keyed independent tasks concurrently.
pipeline(name, items, stages)Apply ordered stages to keyed items.
phase(name)Record progress telemetry.
log(message)Add a TUI-only transcript entry, capped at 4 KB.
checkpoint(input)Wait for an approval or rejection.
withWorktree(name, callback)Materialize one explicitly named worktree scope before invoking the callback; pass the callback a frozen { path, branch } reference.

shell(command, options) inherits the workflow cwd, or the active named withWorktree(name, callback) cwd. Options are timeoutMs and string-valued environment overrides merged over the host environment. A nonzero exit is a result, not a workflow failure; launch failures and timeouts use SHELL_FAILED. Output is not shell-truncated, but a result that exceeds the 10 MB RPC boundary fails with RPC_LIMIT_EXCEEDED.

return await withWorktree("fix-tests", async () => {
  for (let attempt = 1; attempt <= 5; attempt += 1) {
    const tests = await shell("yarn test");
    if (tests.exitCode === 0) return tests;
    await agent("Fix the test failures:\n\n" + (tests.stderr || tests.stdout));
  }
  return shell("yarn test");
});

Completed shell results are journaled only after process exit and RPC validation, so cold resume replays them without rerunning the command. A host crash after a command performs side effects but before its result is journaled can run it again; use shell() primarily for verification and bounded command gates, not exactly-once mutations.

Agent options

Use label as an optional non-empty display name. If omitted, agents use their role name, or the effective model name for role-less agents. Use model as a configured model alias or provider/model, optionally with a thinking suffix such as :high. Other options are thinking, role, tools, outputSchema, retries, timeoutMs, and the canonical withWorktree(name, callback) scope. A role owns its model, thinking, and tools policy, so do not combine role with those three overrides.

const report = await agent("Review the implementation", {
  label: "Implementation review",
  model: "openai-codex/gpt-5.6-sol:high",
  tools: ["read", "grep"],
});

Results are bare values. With outputSchema, the native agent submits the schema through workflow_result; prose is not parsed as structured output. The serialized result and every worker RPC value must fit within 10 MB.

Composition

const reports = await parallel("review", {
  api: () => agent("Inspect the API"),
  ui: () => agent("Inspect the UI"),
});
return agent(prompt("Combine these reports:\n{reports}", { reports }));

Calls from one source location must not race outside parallel or pipeline. Their structural keys make replay deterministic. Ordinary branch failures wait for sibling work to settle; cancellation is immediate.

Extensions, reusable functions, and roles

Trusted Pi extensions can register reusable workflow functions, run-scoped variables, and per-agent setup hooks. The complete field-level authoring contract, validation rules, replay semantics, and copy-pasteable examples now live on the dedicated Extensions page.

This anchor remains for compatibility with existing links. Keep role-file format documentation on this page and use the extension resources section for the boundary between author-facing extension APIs, roles, and settings-backed model aliases.

Role files

Global roles live under <agentDir>/pi-extensible-workflows/roles/<name>.md, defaulting to ~/.pi/agent/pi-extensible-workflows/roles/<name>.md; set PI_CODING_AGENT_DIR to change it. Trusted project roles live under <cwd>/.pi/pi-extensible-workflows/roles/<name>.md. A workflow extension can provide packaged defaults with roleDirectories, using absolute filesystem paths or file: URLs such as new URL("./roles", import.meta.url); global and trusted project roles override extension roles with the same name. Each registered extension directory must exist and be a directory, and duplicate role names across extension directories are rejected rather than resolved by load order.

---
description: Reviews code for correctness
model: anthropic/claude-fable-5
thinking: high
tools: [read, grep]
---
Focus on correctness and regressions.

Role frontmatter may also define disabledAgentResources with optional skills and extensions arrays. These exclusions merge with global and trusted-project policy only for agents using that role; unroled agents and other roles are unchanged.

---
disabledAgentResources:
  skills: [interactive-skill]
  extensions: [../extensions/review-only.ts]
---

Skill selectors match names and extension selectors match normalized paths using the same ordered Minimatch and negation rules as settings. Relative extension path patterns resolve from the role file, with the same ~, file://, and prefix canonicalization rules; configured selector order is preserved.

When role is present, omit model, thinking, and tools from the agent call. The role body is appended to the native Pi system prompt.

Prioritized agent setup hooks

Security: setup hooks are trusted extension code. They run after ordinary workflow model, tool, cwd, and role policy validation, but before the native Pi session is created. Hooks can bypass those boundaries by mutating the prompt, raw options, resolved session input, or session factory; never register untrusted code as a setup hook.

TypeScript: hook callbacks are contextually typed. agent.options is the workflow-owned AgentOptions object, including its typed core fields and JSON-compatible extension options. agent.sessionInput is the workflow-owned pre-session input with typed cwd, model, tools, and system-prompt fields; its customTools and extensionFactories fields use Pi's ToolDefinition and InlineExtension types. agent.createSession is the typed workflow session factory and is callable with that session input; it is not Pi's raw CreateAgentSessionOptions object.

Register named hooks with an optional finite priority. Hooks run in ascending priority order (default 10); equal priorities use the stable hook name as the tie-breaker. Each retry starts from a fresh baseline and runs every hook again. Hooks receive the workflow cancellation signal, and their asynchronous runtime is not charged against timeoutMs. A thrown hook error fails the agent immediately, prevents later hooks and session creation, and is not retried as a native-session failure.

registerWorkflowExtension({
  version: "1.0.0",
  headline: "Advisor setup",
  description: "Adds optional advisor behavior",
  agentSetupHooks: {
    advisor: {
      priority: 10,
      async setup(agent, context) {
        if (agent.options.advisor !== true) return;
        agent.sessionInput.extensionFactories ??= [];
        agent.sessionInput.extensionFactories.push(scopedAdvisorFactory);
        if (context.signal.aborted) return;
      },
    },
  },
});

Unknown JSON-compatible agent options are preserved for setup hooks and native setup, but are not inherited by child agents. Inspection records only executed hook names and the effective model, tools, and cwd; it never serializes hook functions, opaque setup values, or raw options. Cold recovery reruns the hooks currently registered with the host; persisted runs do not pin an extension version.

Lifecycle, checkpoints, and worktrees

Run states are queued, running, pausing, paused, awaiting_input, budget_exhausted, completed, failed, stopped, and interrupted. budget_exhausted is terminal for the current execution segment but can be resumed with an acceptable budget. Agent states include queued, running, waiting_for_child, paused, retrying, completed, failed, and cancelled.

Use a checkpoint when an agent must wait for a human or parent decision:

const decision = await checkpoint({
  name: "ship",
  prompt: "Ship this build?",
  context: { commit: args.commit },
});
if (decision !== "approved") return { approved: false };

Checkpoint prompts are limited to 1 KB and serialized context to 4 KB. Sandboxed workflow scripts receive "approved" or "rejected" from direct checkpoint(); registered functions receive a boolean from WorkflowOrchestrationContext.checkpoint. Responses are journaled and replay after cold recovery.

Use withWorktree(name, callback) for canonical worktree scopes: put collaborating top-level agents in one named callback, or put each independent parallel branch in its own named callback. The runtime creates and resolves each deterministic owned branch and worktree before invoking the callback; the callback receives a frozen public reference containing only path and branch. The caller branch is unchanged, children and retries reuse the enclosing scope, and recovery preserves the existing ownership and cleanup behavior. A later run can pass the previous terminal run's ID as parentRunId to reuse matching named scopes without copying, merging, or creating another branch. Borrowed bindings are validated on cold resume and remain invalid if their originating run is deleted.

const results = await parallel("issues", {
  issue81: () =>
    withWorktree("issue-81", async ({ path, branch }) => {
      const devResult = await developUntilApproved({
        task: "Resolve issue #81",
      });
      const worktree = { path, branch };
      return { devResult, worktree };
    }),
});
return agent(prompt("Merge these verified worktrees: {results}", { results }));

Agent grouping and drilldown

The live workflow card and /workflow group agents by their structural workflow path and include registered-function context as a breadcrumb. Groups remain stable as later agents start, creation order is preserved inside each group, and actual parent-child agents remain nested.

The run navigator uses a phase-first tree: phase nodes contain structural operation-path nodes and agent leaves. On wide terminals, the selected node's details appear beside the tree; on narrow terminals, the first Enter opens details and a second Enter on an agent opens agent actions, while Escape returns to the tree. Press a for run actions. The run script and completed top-level agent results can be opened in the configured external editor from the TUI; each uses a read-only temporary copy, with .js, .json, or .md content types. Phase and operation nodes have no artifact action. Other agent actions include forking an attempt in a Herdr pane when available, copying its agent ID, and, when it owns a worktree, copying that branch or worktree path. Worktree ownership IDs and per-worktree copy actions are not exposed in the top-level run actions.

Run storage and executed source

Runs are stored under ~/.pi/workflows/projects/<cwd-slug>-<cwd-hash>/sessions/<session-id>/runs/<run-id>/. Identity checks use the resolved launch cwd and Pi session ID.

Every new run contains workflow.js with the exact UTF-8 JavaScript source that was preflighted and executed. It is an immutable inspection artifact written during atomic run creation and is opened through a read-only temporary copy. snapshot.json remains authoritative for execution and resume; historical runs without workflow.js remain loadable.

Launch snapshots use identity version 5. Cold resume rejects older snapshots, including v4 snapshots created with the previous worktree or registered-function naming contracts, with RESUME_INCOMPATIBLE; relaunch the workflow instead.

Each run's private system-prompts.json records the full effective system prompt at every native Pi agent_start, after before_agent_start extensions have run. Entries include the native session ID, attempt, turn, SHA-256 digest, and prompt. Native Pi JSONL transcripts intentionally omit system prompts.

Run directories use mode 0700 and the prompt artifact uses 0600. Treat it as sensitive because it can contain role bodies, project instructions, tool guidance, and extension modifications. Confirmed run deletion removes it; native Pi transcripts remain in Pi session storage.

Workflow lifecycle events

The package exports WORKFLOW_RUN_STARTED_EVENT, WORKFLOW_RUN_RESUMED_EVENT, WORKFLOW_RUN_STATE_CHANGED_EVENT, WORKFLOW_RUN_COMPLETED_EVENT, WORKFLOW_RUN_FAILED_EVENT, WORKFLOW_AGENT_STATE_CHANGED_EVENT, WORKFLOW_PHASE_CHANGED_EVENT, WORKFLOW_CHECKPOINT_STATE_CHANGED_EVENT, WORKFLOW_BUDGET_EVENT, and WORKFLOW_WORKTREE_CREATED_EVENT, plus matching TypeScript payload types such as WorkflowEventBase and WorkflowRunCompletedEvent.

Subscribe through pi.events. Events are in-process, best-effort, emitted after the corresponding run artifact or state persistence, and ordered within each run. Payloads contain correlation and safe inspection metadata only: prompts, responses, results, tool data, environment values, and workflow source are never included. Manual stop, interruption, pause, awaiting input, and budget exhaustion are state changes, not run completion or failure.

EventFires when
WORKFLOW_RUN_STARTED_EVENTAfter a new run is created and execution begins.
WORKFLOW_RUN_RESUMED_EVENTAfter a run transitions from paused, interrupted, or budget_exhausted to running.
WORKFLOW_RUN_STATE_CHANGED_EVENTAfter every persisted run-state transition.
WORKFLOW_RUN_COMPLETED_EVENTAfter the result is saved and the run enters completed.
WORKFLOW_RUN_FAILED_EVENTAfter a run enters failed; it does not fire for stopped, interrupted, or budget_exhausted.
WORKFLOW_AGENT_STATE_CHANGED_EVENTAfter persisted agent state or attempt-count data changes.
WORKFLOW_PHASE_CHANGED_EVENTAfter the persisted phase changes to a different value.
WORKFLOW_CHECKPOINT_STATE_CHANGED_EVENTAfter a checkpoint is persisted as awaiting, approved, or rejected.
WORKFLOW_BUDGET_EVENTAfter each newly persisted budget event is recorded.
WORKFLOW_WORKTREE_CREATED_EVENTOnce after a new worktree is persisted for an owner.
pi.events.on(WORKFLOW_RUN_COMPLETED_EVENT, (event: WorkflowRunCompletedEvent) =>
  console.log(event.runId, event.resultPath),
);

Operations and debugging

Doctor and inspector

npx pi-extensible-workflows doctor
npx pi-extensible-workflows doctor cleanup [--older-than-days <days>] [--yes]
npx pi-extensible-workflows inspect [session-id]
npx pi-extensible-workflows transcript <session-file>
npx pi-extensible-workflows run <workflow-name> [workflow arguments]
npx pi-extensible-workflows export <workflow-name> [--name <command>] [--output <path>] [--force]
npm run inspect -- [session-id]

doctor is read-only and checks Pi trust and resources, active tools, models, roles, settings, extension load failures, and registered functions. It exits nonzero only for errors. doctor cleanup is separate, explicit, and dry-run-first: it previews terminal runs older than 90 days across the current project's stored sessions and deletes only with --yes, protecting active, corrupt, leased, and dependency-linked runs. inspect opens the read-only terminal inspector; the detail view includes agents, retries, models, costs, prompts, and the syntax-highlighted workflow script. transcript renders the existing session transcript format to stdout.

run accepts flat workflow arguments plus --approve or --no-approve trust overrides. The generated export launcher forwards later arguments to run. Before --, these are launcher options; -- ends launcher option parsing and passes later tokens to workflow input. The headless run command and generated export launchers cannot execute workflows containing checkpoints; use the Pi workflow tool/UI path for checkpointed workflows.

/workflow controls

/workflow doctor
/workflow model-aliases
/workflow pause <run-id>
/workflow resume <run-id>
/workflow stop <run-id>
/workflow approve <run-id> <checkpoint-name>
/workflow reject <run-id> <checkpoint-name>
/workflow delete <run-id>

approve and reject are checkpoint-only controls. For budget proposals, use workflow_respond with the exact proposalId or the navigator's budget controls.

The navigator discovers only runs from the exact current cwd and Pi session. It exposes Model aliases even when no runs exist, groups agents by structural scope, and shows budget status and adjustment controls only when relevant. The selected-run overlay presents the phase-first tree of declared and observed phase occurrences with explicit status and agent counts, while legacy snapshots fall back to observed data safely. In a Herdr-managed session, it can open the aggregate inspector and fork agent attempts into Pi sessions in a pane targeted by HERDR_PANE_ID; running forks require confirmation and use read-only tools. Pause is cooperative, stop is immediate and irreversible, and delete is available only for terminal runs after confirmation.

Invocation options override settings defaults, and the session-wide active-agent ceiling is 16. Host-facing failures are WorkflowError values with stable codes such as CONFIG_ERROR, INVALID_SYNTAX, UNKNOWN_MODEL, UNKNOWN_TOOL, AGENT_FAILED, AGENT_TIMEOUT, RESULT_INVALID, CANCELLED, WORKTREE_FAILED, BUDGET_EXHAUSTED, and RESUME_INCOMPATIBLE. Fix the reported trust, model, tool, role, budget, settings, or script issue before retrying.

Evaluation and package checks

Deterministic evaluation helpers use a fake bridge and make zero model calls during normal tests. Model-backed evaluations are opt-in and require an explicit provider and model.

npm run check
npm run acceptance
npm run evals -- --provider "$PROVIDER" --model "$MODEL" --case direct-answer,parallel
PI_WORKFLOW_EVAL_AMBIENT=1 npm run evals:ambient -- --provider "$PROVIDER" --model "$MODEL"

The ambient tier is separately opt-in. Do not enable it in ordinary CI unless its external model cost and cleanup are intentional.